Home messages business logic, wallet info removed from status bar and general refactoring

This commit is contained in:
Milan Cerovsky 2025-04-10 12:53:34 +02:00
parent 2f12bea435
commit d6d500eaed
143 changed files with 1296 additions and 1206 deletions

View File

@ -29,5 +29,7 @@ interface PreferenceProvider {
fun observe(key: PreferenceKey): Flow<String?>
suspend fun remove(key: PreferenceKey)
suspend fun clearPreferences(): Boolean
}

View File

@ -22,6 +22,6 @@ data class NullableBooleanPreferenceDefault(
preferenceProvider: PreferenceProvider,
newValue: Boolean?
) {
preferenceProvider.putString(key, newValue.toString())
preferenceProvider.putString(key, newValue?.toString())
}
}

View File

@ -0,0 +1,14 @@
package co.electriccoin.zcash.preference.model.entry
import co.electriccoin.zcash.preference.api.PreferenceProvider
import java.time.Instant
class TimestampPreferenceDefault(override val key: PreferenceKey): PreferenceDefault<Instant?> {
override suspend fun getValue(preferenceProvider: PreferenceProvider) =
preferenceProvider.getLong(key)?.let { Instant.ofEpochMilli(it) }
override suspend fun putValue(
preferenceProvider: PreferenceProvider,
newValue: Instant?
) = preferenceProvider.putLong(key, newValue?.toEpochMilli())
}

View File

@ -22,6 +22,10 @@ class MockPreferenceProvider(
// For the mock implementation, does not support observability of changes
override fun observe(key: PreferenceKey): Flow<String?> = flow { emit(getString(key)) }
override suspend fun remove(key: PreferenceKey) {
map.remove(key.key)
}
override suspend fun clearPreferences(): Boolean {
map.clear()
return true

View File

@ -140,6 +140,17 @@ class AndroidPreferenceProvider private constructor(
}.flowOn(dispatcher)
.map { getString(key) }
@SuppressLint("ApplySharedPref")
override suspend fun remove(key: PreferenceKey) {
withContext(dispatcher) {
val editor = sharedPreferences.edit()
editor.remove(key.key)
editor.commit()
}
}
companion object {
suspend fun newStandard(
context: Context,

View File

@ -1,6 +1,5 @@
package co.electriccoin.zcash.ui.design.component
import androidx.annotation.IntRange
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ProgressIndicatorDefaults
@ -36,8 +35,8 @@ fun ZashiCircularProgressIndicator(
}
@Composable
fun ZashiCircularProgressIndicator(
@IntRange(from = 0, to = 100) progressPercent: Int,
fun ZashiCircularProgressIndicatorByPercent(
progressPercent: Float,
modifier: Modifier = Modifier,
colors: ZashiCircularProgressIndicatorColors =
LocalZashiCircularProgressIndicatorColors.current

View File

@ -28,8 +28,7 @@ val DarkZashiColorsInternal =
textError = ErrorRed.`300`,
textLink = HyperBlue.`300`,
textLight = Shark.`50`,
textLightSupport = Shark.`200`,
textOpposite = Base.Bone
textLightSupport = Shark.`200`
),
Btns =
Btns(

View File

@ -28,8 +28,7 @@ val LightZashiColorsInternal =
textError = ErrorRed.`500`,
textLink = HyperBlue.`500`,
textLight = Gray.`25`,
textLightSupport = Gray.`200`,
textOpposite = Base.Bone
textLightSupport = Gray.`200`
),
Btns =
Btns(

View File

@ -145,7 +145,7 @@ internal object Indigo {
val `950` = Color(0xFF1F235B)
}
object Purple {
internal object Purple {
val `25` = Color(0xFFFAFAFF)
val `50` = Color(0xFFF4F3FF)
val `100` = Color(0xFFEBE9FE)

View File

@ -56,8 +56,7 @@ data class Text(
val textError: Color,
val textLink: Color,
val textLight: Color,
val textLightSupport: Color,
val textOpposite: Color
val textLightSupport: Color
)
@Immutable

View File

@ -3,7 +3,6 @@ package co.electriccoin.zcash.ui.integration.test.screen.scan.view
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.integration.test.common.getPermissionNegativeButtonUiObject
import co.electriccoin.zcash.ui.integration.test.common.getPermissionPositiveButtonUiObject
@ -60,7 +59,6 @@ class ScanViewTestSetup(
onScanStateChange = {
scanState.set(it)
},
topAppBarSubTitleState = TopAppBarSubTitleState.None,
validationResult = ScanValidationState.VALID
)
}

View File

@ -2,7 +2,6 @@ package co.electriccoin.zcash.ui.screen.about.view
import androidx.compose.material3.SnackbarHostState
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.screen.support.model.ConfigInfo
@ -28,7 +27,6 @@ class AboutViewTestSetup(
configInfo = configInfo,
onPrivacyPolicy = {},
snackbarHostState = SnackbarHostState(),
topAppBarSubTitleState = TopAppBarSubTitleState.None,
versionInfo = versionInfo,
)
}

View File

@ -3,7 +3,6 @@ package co.electriccoin.zcash.ui.screen.exportdata.view
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
@ -46,7 +45,6 @@ class ExportPrivateDataViewTestSetup(
onConfirm = {
onConfirmCount.incrementAndGet()
},
topAppBarSubTitleState = TopAppBarSubTitleState.None,
)
}

View File

@ -3,7 +3,6 @@ package co.electriccoin.zcash.ui.screen.scan.view
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.screen.scan.model.ScanScreenState
import co.electriccoin.zcash.ui.screen.scan.model.ScanValidationState
@ -30,7 +29,7 @@ class ScanViewBasicTestSetup(
@Suppress("TestFunctionName")
fun DefaultContent() {
Scan(
validationResult = ScanValidationState.VALID,
snackbarHostState = SnackbarHostState(),
onBack = {
onBackCount.incrementAndGet()
},
@ -40,8 +39,7 @@ class ScanViewBasicTestSetup(
onScanStateChange = {
scanState.set(it)
},
snackbarHostState = SnackbarHostState(),
topAppBarSubTitleState = TopAppBarSubTitleState.None,
validationResult = ScanValidationState.VALID,
)
}

View File

@ -2,7 +2,6 @@ package co.electriccoin.zcash.ui.screen.settings
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.design.component.listitem.ZashiListItemState
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.design.util.stringRes
@ -149,7 +148,6 @@ class SettingsViewTestSetup(
),
)
),
topAppBarSubTitleState = TopAppBarSubTitleState.None,
)
}
}

View File

@ -6,6 +6,8 @@ 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.WalletBackupDataSource
import co.electriccoin.zcash.ui.common.datasource.WalletBackupDataSourceImpl
import co.electriccoin.zcash.ui.common.datasource.ZashiSpendingKeyDataSource
import co.electriccoin.zcash.ui.common.datasource.ZashiSpendingKeyDataSourceImpl
import org.koin.core.module.dsl.singleOf
@ -18,4 +20,5 @@ val dataSourceModule =
singleOf(::ZashiSpendingKeyDataSourceImpl) bind ZashiSpendingKeyDataSource::class
singleOf(::ProposalDataSourceImpl) bind ProposalDataSource::class
singleOf(::RestoreTimestampDataSourceImpl) bind RestoreTimestampDataSource::class
singleOf(::WalletBackupDataSourceImpl) bind WalletBackupDataSource::class
}

View File

@ -12,8 +12,18 @@ import co.electriccoin.zcash.ui.common.provider.RestoreTimestampStorageProvider
import co.electriccoin.zcash.ui.common.provider.RestoreTimestampStorageProviderImpl
import co.electriccoin.zcash.ui.common.provider.SelectedAccountUUIDProvider
import co.electriccoin.zcash.ui.common.provider.SelectedAccountUUIDProviderImpl
import co.electriccoin.zcash.ui.common.provider.ShieldFundsRemindMeCountStorageProvider
import co.electriccoin.zcash.ui.common.provider.ShieldFundsRemindMeCountStorageProviderImpl
import co.electriccoin.zcash.ui.common.provider.ShieldFundsRemindMeTimestampStorageProvider
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.WalletBackupFlagStorageProvider
import co.electriccoin.zcash.ui.common.provider.WalletBackupFlagStorageProviderImpl
import co.electriccoin.zcash.ui.common.provider.WalletBackupRemindMeCountStorageProvider
import co.electriccoin.zcash.ui.common.provider.WalletBackupRemindMeCountStorageProviderImpl
import co.electriccoin.zcash.ui.common.provider.WalletBackupRemindMeTimestampStorageProvider
import co.electriccoin.zcash.ui.common.provider.WalletBackupRemindMeTimestampStorageProviderImpl
import org.koin.core.module.dsl.factoryOf
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.bind
@ -30,4 +40,13 @@ val providerModule =
singleOf(::SynchronizerProviderImpl) bind SynchronizerProvider::class
singleOf(::ApplicationStateProviderImpl) bind ApplicationStateProvider::class
factoryOf(::RestoreTimestampStorageProviderImpl) bind RestoreTimestampStorageProvider::class
factoryOf(::ShieldFundsRemindMeCountStorageProviderImpl) bind
ShieldFundsRemindMeCountStorageProvider::class
factoryOf(::ShieldFundsRemindMeTimestampStorageProviderImpl) bind
ShieldFundsRemindMeTimestampStorageProvider::class
factoryOf(::WalletBackupRemindMeCountStorageProviderImpl) bind
WalletBackupRemindMeCountStorageProvider::class
factoryOf(::WalletBackupRemindMeTimestampStorageProviderImpl) bind
WalletBackupRemindMeTimestampStorageProvider::class
factoryOf(::WalletBackupFlagStorageProviderImpl) bind WalletBackupFlagStorageProvider::class
}

View File

@ -23,6 +23,7 @@ import co.electriccoin.zcash.ui.common.usecase.GetCurrentFilteredTransactionsUse
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
import co.electriccoin.zcash.ui.common.usecase.GetKeystoneStatusUseCase
import co.electriccoin.zcash.ui.common.usecase.GetMetadataUseCase
import co.electriccoin.zcash.ui.common.usecase.GetPersistableWalletUseCase
@ -37,7 +38,6 @@ import co.electriccoin.zcash.ui.common.usecase.GetTransactionMetadataUseCase
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.GetWalletStateInformationUseCase
import co.electriccoin.zcash.ui.common.usecase.GetZashiAccountUseCase
import co.electriccoin.zcash.ui.common.usecase.GetZashiSpendingKeyUseCase
import co.electriccoin.zcash.ui.common.usecase.IsCoinbaseAvailableUseCase
@ -46,7 +46,7 @@ import co.electriccoin.zcash.ui.common.usecase.IsRestoreSuccessDialogVisibleUseC
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.NavigateToSeedRecoveryUseCase
import co.electriccoin.zcash.ui.common.usecase.NavigateToWalletBackupUseCase
import co.electriccoin.zcash.ui.common.usecase.NavigateToTaxExportUseCase
import co.electriccoin.zcash.ui.common.usecase.ObserveAddressBookContactsUseCase
import co.electriccoin.zcash.ui.common.usecase.ObserveClearSendUseCase
@ -62,6 +62,7 @@ import co.electriccoin.zcash.ui.common.usecase.ObserveSynchronizerUseCase
import co.electriccoin.zcash.ui.common.usecase.ObserveTransactionSubmitStateUseCase
import co.electriccoin.zcash.ui.common.usecase.ObserveZashiAccountUseCase
import co.electriccoin.zcash.ui.common.usecase.OnAddressScannedUseCase
import co.electriccoin.zcash.ui.common.usecase.OnUserSavedWalletBackupUseCase
import co.electriccoin.zcash.ui.common.usecase.OnZip321ScannedUseCase
import co.electriccoin.zcash.ui.common.usecase.ParseKeystonePCZTUseCase
import co.electriccoin.zcash.ui.common.usecase.ParseKeystoneSignInRequestUseCase
@ -69,6 +70,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.RemindWalletBackupLaterUseCase
import co.electriccoin.zcash.ui.common.usecase.RescanBlockchainUseCase
import co.electriccoin.zcash.ui.common.usecase.ResetInMemoryDataUseCase
import co.electriccoin.zcash.ui.common.usecase.ResetSharedPrefsDataUseCase
@ -122,7 +124,6 @@ val useCaseModule =
factoryOf(::ShareImageUseCase)
factoryOf(::Zip321BuildUriUseCase)
factoryOf(::Zip321ParseUriValidationUseCase)
factoryOf(::GetWalletStateInformationUseCase)
factoryOf(::IsCoinbaseAvailableUseCase)
factoryOf(::GetZashiSpendingKeyUseCase)
factoryOf(::ObservePersistableWalletUseCase)
@ -184,8 +185,11 @@ val useCaseModule =
factoryOf(::IsRestoreSuccessDialogVisibleUseCase)
factoryOf(::ValidateSeedUseCase)
factoryOf(::RestoreWalletUseCase)
factoryOf(::NavigateToSeedRecoveryUseCase)
factoryOf(::NavigateToWalletBackupUseCase)
factoryOf(::GetKeystoneStatusUseCase)
factoryOf(::GetCoinbaseStatusUseCase)
factoryOf(::GetFlexaStatusUseCase)
factoryOf(::GetHomeMessageUseCase)
factoryOf(::OnUserSavedWalletBackupUseCase)
factoryOf(::RemindWalletBackupLaterUseCase)
}

View File

@ -12,10 +12,14 @@ import co.electriccoin.zcash.ui.screen.balances.BalanceViewModel
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
import co.electriccoin.zcash.ui.screen.exchangerate.optin.ExchangeRateOptInViewModel
import co.electriccoin.zcash.ui.screen.exchangerate.settings.ExchangeRateSettingsViewModel
import co.electriccoin.zcash.ui.screen.feedback.viewmodel.FeedbackViewModel
import co.electriccoin.zcash.ui.screen.flexa.FlexaViewModel
import co.electriccoin.zcash.ui.screen.home.HomeViewModel
import co.electriccoin.zcash.ui.screen.home.balance.TransparentBalanceInfoViewModel
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.integrations.IntegrationsViewModel
import co.electriccoin.zcash.ui.screen.qrcode.viewmodel.QrCodeViewModel
import co.electriccoin.zcash.ui.screen.receive.viewmodel.ReceiveViewModel
@ -24,13 +28,13 @@ import co.electriccoin.zcash.ui.screen.restore.date.RestoreBDDateViewModel
import co.electriccoin.zcash.ui.screen.restore.estimation.RestoreBDEstimationViewModel
import co.electriccoin.zcash.ui.screen.restore.height.RestoreBDHeightViewModel
import co.electriccoin.zcash.ui.screen.restore.seed.RestoreSeedViewModel
import co.electriccoin.zcash.ui.screen.restoresuccess.viewmodel.RestoreSuccessViewModel
import co.electriccoin.zcash.ui.screen.restoresuccess.RestoreSuccessViewModel
import co.electriccoin.zcash.ui.screen.reviewtransaction.ReviewTransactionViewModel
import co.electriccoin.zcash.ui.screen.scan.Scan
import co.electriccoin.zcash.ui.screen.scan.viewmodel.ScanViewModel
import co.electriccoin.zcash.ui.screen.scankeystone.viewmodel.ScanKeystonePCZTViewModel
import co.electriccoin.zcash.ui.screen.scankeystone.viewmodel.ScanKeystoneSignInRequestViewModel
import co.electriccoin.zcash.ui.screen.seed.SeedRecoveryViewModel
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
@ -96,7 +100,7 @@ val viewModelModule =
viewModelOf(::IntegrationsViewModel)
viewModelOf(::FlexaViewModel)
viewModelOf(::SendViewModel)
viewModelOf(::SeedRecoveryViewModel)
viewModelOf(::WalletBackupViewModel)
viewModelOf(::FeedbackViewModel)
viewModelOf(::SignKeystoneTransactionViewModel)
viewModelOf(::AccountListViewModel)
@ -144,4 +148,8 @@ val viewModelModule =
viewModelOf(::RestoreBDDateViewModel)
viewModelOf(::RestoreBDEstimationViewModel)
viewModelOf(::TransparentBalanceInfoViewModel)
viewModelOf(::WalletBackupInfoViewModel)
viewModelOf(::ExchangeRateOptInViewModel)
viewModelOf(::ExchangeRateSettingsViewModel)
viewModelOf(::WalletBackupDetailViewModel)
}

View File

@ -67,19 +67,19 @@ import co.electriccoin.zcash.ui.screen.exportdata.WrapExportPrivateData
import co.electriccoin.zcash.ui.screen.feedback.WrapFeedback
import co.electriccoin.zcash.ui.screen.flexa.FlexaViewModel
import co.electriccoin.zcash.ui.screen.home.AndroidHome
import co.electriccoin.zcash.ui.screen.home.AndroidSeedBackupInfo
import co.electriccoin.zcash.ui.screen.home.AndroidWalletDisconnectedInfo
import co.electriccoin.zcash.ui.screen.home.AndroidWalletRestoringInfo
import co.electriccoin.zcash.ui.screen.home.AndroidWalletSyncingInfo
import co.electriccoin.zcash.ui.screen.home.AndroidWalletUpdatingInfo
import co.electriccoin.zcash.ui.screen.home.backup.AndroidWalletBackupInfo
import co.electriccoin.zcash.ui.screen.home.disconnected.AndroidWalletDisconnectedInfo
import co.electriccoin.zcash.ui.screen.home.restoring.AndroidWalletRestoringInfo
import co.electriccoin.zcash.ui.screen.home.syncing.AndroidWalletSyncingInfo
import co.electriccoin.zcash.ui.screen.home.updating.AndroidWalletUpdatingInfo
import co.electriccoin.zcash.ui.screen.home.Home
import co.electriccoin.zcash.ui.screen.home.SeedBackupInfo
import co.electriccoin.zcash.ui.screen.home.WalletDisconnectedInfo
import co.electriccoin.zcash.ui.screen.home.WalletRestoringInfo
import co.electriccoin.zcash.ui.screen.home.WalletSyncingInfo
import co.electriccoin.zcash.ui.screen.home.WalletUpdatingInfo
import co.electriccoin.zcash.ui.screen.home.balance.AndroidTransparentBalanceInfo
import co.electriccoin.zcash.ui.screen.home.balance.TransparentBalanceInfo
import co.electriccoin.zcash.ui.screen.home.backup.SeedBackupInfo
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.integrations.AndroidDialogIntegrations
import co.electriccoin.zcash.ui.screen.integrations.AndroidIntegrations
import co.electriccoin.zcash.ui.screen.integrations.DialogIntegrations
@ -99,10 +99,10 @@ import co.electriccoin.zcash.ui.screen.scankeystone.ScanKeystonePCZTRequest
import co.electriccoin.zcash.ui.screen.scankeystone.ScanKeystoneSignInRequest
import co.electriccoin.zcash.ui.screen.scankeystone.WrapScanKeystonePCZTRequest
import co.electriccoin.zcash.ui.screen.scankeystone.WrapScanKeystoneSignInRequest
import co.electriccoin.zcash.ui.screen.seed.AndroidSeedRecovery
import co.electriccoin.zcash.ui.screen.seed.SeedRecovery
import co.electriccoin.zcash.ui.screen.seed.backup.AndroidSeedBackup
import co.electriccoin.zcash.ui.screen.seed.backup.SeedBackup
import co.electriccoin.zcash.ui.screen.walletbackup.AndroidWalletBackup
import co.electriccoin.zcash.ui.screen.walletbackup.WalletBackup
import co.electriccoin.zcash.ui.screen.home.backup.AndroidWalletBackupDetail
import co.electriccoin.zcash.ui.screen.home.backup.WalletBackupDetail
import co.electriccoin.zcash.ui.screen.selectkeystoneaccount.AndroidSelectKeystoneAccount
import co.electriccoin.zcash.ui.screen.selectkeystoneaccount.SelectKeystoneAccount
import co.electriccoin.zcash.ui.screen.send.Send
@ -227,8 +227,8 @@ internal fun MainActivity.Navigation() {
composable(CHOOSE_SERVER) {
WrapChooseServer()
}
composable<SeedRecovery> {
AndroidSeedRecovery()
composable<WalletBackup> {
AndroidWalletBackup(it.toRoute())
}
composable(SUPPORT) {
// Pop back stack won't be right if we deep link into support
@ -410,8 +410,8 @@ internal fun MainActivity.Navigation() {
) {
AndroidSeedInfo()
}
composable<SeedBackup> {
AndroidSeedBackup(it.toRoute())
composable<WalletBackupDetail> {
AndroidWalletBackupDetail(it.toRoute())
}
dialog<SeedBackupInfo>(
dialogProperties =
@ -420,7 +420,7 @@ internal fun MainActivity.Navigation() {
dismissOnClickOutside = false
)
) {
AndroidSeedBackupInfo()
AndroidWalletBackupInfo()
}
dialog<TransparentBalanceInfo>(
dialogProperties =

View File

@ -8,12 +8,10 @@ import co.electriccoin.zcash.preference.model.entry.BooleanPreferenceDefault
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.NavigationTargets.SETTINGS
import co.electriccoin.zcash.ui.common.model.KeystoneAccount
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.common.model.WalletAccount
import co.electriccoin.zcash.ui.common.model.ZashiAccount
import co.electriccoin.zcash.ui.common.usecase.GetSelectedWalletAccountUseCase
import co.electriccoin.zcash.ui.common.usecase.GetWalletAccountsUseCase
import co.electriccoin.zcash.ui.common.usecase.GetWalletStateInformationUseCase
import co.electriccoin.zcash.ui.design.R
import co.electriccoin.zcash.ui.design.component.IconButtonState
import co.electriccoin.zcash.ui.design.util.stringRes
@ -33,7 +31,6 @@ import kotlinx.coroutines.launch
class ZashiTopAppBarViewModel(
getWalletAccountUseCase: GetWalletAccountsUseCase,
getSelectedWalletAccount: GetSelectedWalletAccountUseCase,
getWalletStateInformation: GetWalletStateInformationUseCase,
private val standardPreferenceProvider: StandardPreferenceProvider,
private val navigationRouter: NavigationRouter,
) : ViewModel() {
@ -43,24 +40,21 @@ class ZashiTopAppBarViewModel(
combine(
getSelectedWalletAccount.observe().filterNotNull(),
isHideBalances,
getWalletStateInformation.observe()
) { currentAccount, isHideBalances, walletState ->
createState(currentAccount, isHideBalances, walletState)
) { currentAccount, isHideBalances ->
createState(currentAccount, isHideBalances)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
initialValue =
createState(
currentAccount = getWalletAccountUseCase.observe().value?.firstOrNull { it.isSelected },
isHideBalances = isHideBalances.value,
topAppBarSubTitleState = getWalletStateInformation.observe().value
isHideBalances = isHideBalances.value
)
)
private fun createState(
currentAccount: WalletAccount?,
isHideBalances: Boolean?,
topAppBarSubTitleState: TopAppBarSubTitleState
isHideBalances: Boolean?
) = ZashiMainTopAppBarState(
accountSwitchState =
AccountSwitchState(
@ -88,19 +82,7 @@ class ZashiTopAppBarViewModel(
icon = R.drawable.ic_app_bar_settings,
onClick = ::onSettingsClicked,
contentDescription = stringRes(co.electriccoin.zcash.ui.R.string.settings_menu_content_description)
),
subtitle =
when (topAppBarSubTitleState) {
TopAppBarSubTitleState.Disconnected ->
stringRes(
co.electriccoin.zcash.ui.R.string.disconnected_label_new,
)
TopAppBarSubTitleState.Restoring ->
stringRes(
co.electriccoin.zcash.ui.R.string.restoring_wallet_label_new,
)
TopAppBarSubTitleState.None -> null
}
)
)
private fun onAccountTypeClicked() = navigationRouter.forward(AccountList)

View File

@ -15,14 +15,12 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import co.electriccoin.zcash.ui.common.appbar.ZashiMainTopAppBarState.AccountType
import co.electriccoin.zcash.ui.design.R
@ -32,10 +30,6 @@ import co.electriccoin.zcash.ui.design.component.ZashiSmallTopAppBar
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.StringResource
import co.electriccoin.zcash.ui.design.util.getValue
import co.electriccoin.zcash.ui.design.util.stringRes
@Composable
fun ZashiTopAppBarWithAccountSelection(
@ -61,16 +55,6 @@ fun ZashiTopAppBarWithAccountSelection(
AccountSwitch(state.accountSwitchState)
},
)
if (state.subtitle != null) {
Text(
modifier = Modifier.align(Alignment.BottomCenter),
text = state.subtitle.getValue().uppercase(),
style = ZashiTypography.textXs,
fontWeight = FontWeight.Normal,
color = ZashiColors.Text.textQuaternary
)
}
}
}
@ -128,13 +112,9 @@ private fun AccountSwitch(state: AccountSwitchState) {
data class ZashiMainTopAppBarState(
val accountSwitchState: AccountSwitchState,
val balanceVisibilityButton: IconButtonState,
val settingsButton: IconButtonState,
val subtitle: StringResource?
val settingsButton: IconButtonState
) {
enum class AccountType {
ZASHI,
KEYSTONE
}
enum class AccountType { ZASHI, KEYSTONE }
}
data class AccountSwitchState(
@ -155,8 +135,7 @@ private fun ZashiMainTopAppBarPreview() =
onAccountTypeClick = {}
),
balanceVisibilityButton = IconButtonState(R.drawable.ic_app_bar_balances_hide) {},
settingsButton = IconButtonState(R.drawable.ic_app_bar_settings) {},
subtitle = null
settingsButton = IconButtonState(R.drawable.ic_app_bar_settings) {}
)
)
}
@ -174,8 +153,7 @@ private fun KeystoneMainTopAppBarPreview() =
onAccountTypeClick = {},
),
balanceVisibilityButton = IconButtonState(R.drawable.ic_app_bar_balances_hide) {},
settingsButton = IconButtonState(R.drawable.ic_app_bar_settings) {},
subtitle = null
settingsButton = IconButtonState(R.drawable.ic_app_bar_settings) {}
)
)
}
@ -193,8 +171,7 @@ private fun MainTopAppBarWithSubtitlePreview() =
onAccountTypeClick = {},
),
balanceVisibilityButton = IconButtonState(R.drawable.ic_app_bar_balances_hide) {},
settingsButton = IconButtonState(R.drawable.ic_app_bar_settings) {},
subtitle = stringRes("Subtitle")
settingsButton = IconButtonState(R.drawable.ic_app_bar_settings) {}
)
)
}

View File

@ -26,7 +26,6 @@ fun ZashiTopAppbar(
) {
ZashiSmallTopAppBar(
title = title?.getValue(),
subtitle = state?.subtitle?.getValue(),
navigationAction = {
ZashiTopAppBarBackNavigation(
onBack = onBack,

View File

@ -0,0 +1,35 @@
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
}

View File

@ -0,0 +1,100 @@
package co.electriccoin.zcash.ui.common.datasource
import co.electriccoin.zcash.ui.common.provider.WalletBackupFlagStorageProvider
import co.electriccoin.zcash.ui.common.provider.WalletBackupRemindMeCountStorageProvider
import co.electriccoin.zcash.ui.common.provider.WalletBackupRemindMeTimestampStorageProvider
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 WalletBackupDataSource {
fun observe(): Flow<WalletBackupAvailability>
suspend fun onUserSavedWalletBackup()
suspend fun remindMeLater()
}
class WalletBackupDataSourceImpl(
private val walletBackupFlagStorageProvider: WalletBackupFlagStorageProvider,
private val walletBackupRemindMeCountStorageProvider: WalletBackupRemindMeCountStorageProvider,
private val walletBackupRemindMeTimestampStorageProvider: WalletBackupRemindMeTimestampStorageProvider
) : WalletBackupDataSource {
@OptIn(ExperimentalCoroutinesApi::class)
override fun observe(): Flow<WalletBackupAvailability> = combine(
walletBackupFlagStorageProvider.observe(),
walletBackupRemindMeCountStorageProvider.observe(),
walletBackupRemindMeTimestampStorageProvider.observe()
) { isBackedUp, count, timestamp ->
Triple(isBackedUp, count, timestamp)
}.flatMapLatest { (isBackedUp, count, timestamp) ->
when {
isBackedUp -> flowOf(WalletBackupAvailability.Unavailable)
timestamp == null -> flowOf(WalletBackupAvailability.Available(WalletBackupLockoutDuration.ONE_DAY))
count == 1 -> calculateNext(
lastTimestamp = timestamp,
lastLockoutDuration = WalletBackupLockoutDuration.ONE_DAY,
nextLockoutDuration = WalletBackupLockoutDuration.TWO_DAYS
)
else -> calculateNext(
lastTimestamp = timestamp,
lastLockoutDuration = if (count == 2) {
WalletBackupLockoutDuration.TWO_DAYS
} else {
WalletBackupLockoutDuration.THREE_DAYS
},
nextLockoutDuration = WalletBackupLockoutDuration.THREE_DAYS
)
}
}
override suspend fun onUserSavedWalletBackup() {
walletBackupFlagStorageProvider.store(true)
}
override suspend fun remindMeLater() {
val count = walletBackupRemindMeCountStorageProvider.get()
val timestamp = Instant.now()
walletBackupRemindMeCountStorageProvider.store(count + 1)
walletBackupRemindMeTimestampStorageProvider.store(timestamp)
}
private fun calculateNext(
lastTimestamp: Instant,
lastLockoutDuration: WalletBackupLockoutDuration,
nextLockoutDuration: WalletBackupLockoutDuration
): Flow<WalletBackupAvailability> {
val nextAvailableTimestamp = lastTimestamp.plusMillis(lastLockoutDuration.duration.inWholeMilliseconds)
val now = Instant.now()
return if (nextAvailableTimestamp > now) {
flow {
val remaining = nextAvailableTimestamp.toEpochMilli() - now.toEpochMilli()
emit(WalletBackupAvailability.Unavailable)
delay(remaining)
emit(WalletBackupAvailability.Available(nextLockoutDuration))
}
} else {
flowOf(WalletBackupAvailability.Available(nextLockoutDuration))
}
}
}
sealed interface WalletBackupAvailability {
data class Available(val lockoutDuration: WalletBackupLockoutDuration) : WalletBackupAvailability
data object Unavailable : WalletBackupAvailability
}
enum class WalletBackupLockoutDuration(val duration: Duration) {
ONE_DAY(10.seconds),
TWO_DAYS(20.seconds),
THREE_DAYS(30.seconds)
}

View File

@ -19,16 +19,3 @@ data class WalletSnapshot(
val progress: PercentDecimal,
val synchronizerError: SynchronizerError?
)
// TODO [#1370]: WalletSnapshot.canSpend() calculation limitation
// TODO [#1370]: https://github.com/Electric-Coin-Company/zashi-android/issues/1370
// Note this check is not entirely correct - it does not calculate the resulting fee using the new Proposal API. It's
// fine for now, but it's subject to improvement later once we figure out how to handle it in such cases.
fun WalletSnapshot.canSpend(amount: Zatoshi): Boolean = spendableBalance() >= amount
fun WalletSnapshot.totalBalance() = orchardBalance.total + (saplingBalance?.total ?: Zatoshi(0)) + transparentBalance
// Note that considering both to be spendable is subject to change.
// The user experience could be confusing, and in the future we might prefer to ask users
// to transfer their balance to the latest balance type to make it spendable.
fun WalletSnapshot.spendableBalance() = orchardBalance.available + (saplingBalance?.available ?: Zatoshi(0))

View File

@ -0,0 +1,37 @@
package co.electriccoin.zcash.ui.common.provider
import co.electriccoin.zcash.preference.PreferenceHolder
import co.electriccoin.zcash.preference.api.PreferenceProvider
import co.electriccoin.zcash.preference.model.entry.BooleanPreferenceDefault
import co.electriccoin.zcash.preference.model.entry.PreferenceKey
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow
interface BooleanStorageProvider {
suspend fun get(): Boolean
suspend fun store(flag: Boolean)
fun observe(): Flow<Boolean>
suspend fun clear()
}
abstract class BaseBooleanStorageProvider(key: PreferenceKey) : BooleanStorageProvider {
protected abstract val preferenceHolder: PreferenceHolder
private val default = BooleanPreferenceDefault(key = key, defaultValue = DEFAULT)
private suspend fun getPreferenceProvider(): PreferenceProvider = preferenceHolder()
override suspend fun get(): Boolean = default.getValue(getPreferenceProvider())
override suspend fun store(flag: Boolean) = default.putValue(getPreferenceProvider(), flag)
override fun observe(): Flow<Boolean> = flow { emitAll(default.observe(getPreferenceProvider())) }
override suspend fun clear() = default.putValue(getPreferenceProvider(), DEFAULT)
}
private const val DEFAULT = false

View File

@ -0,0 +1,37 @@
package co.electriccoin.zcash.ui.common.provider
import co.electriccoin.zcash.preference.PreferenceHolder
import co.electriccoin.zcash.preference.api.PreferenceProvider
import co.electriccoin.zcash.preference.model.entry.IntegerPreferenceDefault
import co.electriccoin.zcash.preference.model.entry.PreferenceKey
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow
interface IntStorageProvider {
suspend fun get(): Int
suspend fun store(amount: Int)
fun observe(): Flow<Int>
suspend fun clear()
}
abstract class BaseIntStorageProvider(key: PreferenceKey) : IntStorageProvider {
protected abstract val preferenceHolder: PreferenceHolder
private val default = IntegerPreferenceDefault(key = key, defaultValue = DEFAULT)
private suspend fun getPreferenceProvider(): PreferenceProvider = preferenceHolder()
override suspend fun get(): Int = default.getValue(getPreferenceProvider())
override suspend fun store(amount: Int) = default.putValue(getPreferenceProvider(), amount)
override fun observe(): Flow<Int> = flow { emitAll(default.observe(getPreferenceProvider())) }
override suspend fun clear() = default.putValue(getPreferenceProvider(), DEFAULT)
}
private const val DEFAULT = 0

View File

@ -1,45 +1,10 @@
package co.electriccoin.zcash.ui.common.provider
import co.electriccoin.zcash.preference.EncryptedPreferenceProvider
import co.electriccoin.zcash.preference.api.PreferenceProvider
import co.electriccoin.zcash.preference.model.entry.PreferenceDefault
import co.electriccoin.zcash.preference.model.entry.PreferenceKey
import java.time.Instant
interface RestoreTimestampStorageProvider {
suspend fun get(): Instant?
suspend fun store(key: Instant)
suspend fun clear()
}
interface RestoreTimestampStorageProvider : TimestampStorageProvider
class RestoreTimestampStorageProviderImpl(
private val encryptedPreferenceProvider: EncryptedPreferenceProvider
) : RestoreTimestampStorageProvider {
private val default = RestoreTimestampPreferenceDefault()
override suspend fun get(): Instant? = default.getValue(encryptedPreferenceProvider())
override suspend fun store(key: Instant) {
default.putValue(encryptedPreferenceProvider(), key)
}
override suspend fun clear() {
default.putValue(encryptedPreferenceProvider(), null)
}
}
private class RestoreTimestampPreferenceDefault : PreferenceDefault<Instant?> {
override val key: PreferenceKey = PreferenceKey("restore_timestamp")
override suspend fun getValue(preferenceProvider: PreferenceProvider) =
preferenceProvider.getLong(key)?.let {
Instant.ofEpochMilli(it)
}
override suspend fun putValue(
preferenceProvider: PreferenceProvider,
newValue: Instant?
) = preferenceProvider.putLong(key, newValue?.toEpochMilli())
}
override val preferenceHolder: EncryptedPreferenceProvider
) : BaseTimestampStorageProvider(PreferenceKey("restore_timestamp")), RestoreTimestampStorageProvider

View File

@ -0,0 +1,39 @@
package co.electriccoin.zcash.ui.common.provider
import cash.z.ecc.android.sdk.model.AccountUuid
import co.electriccoin.zcash.preference.EncryptedPreferenceProvider
import co.electriccoin.zcash.preference.api.PreferenceProvider
import co.electriccoin.zcash.preference.model.entry.IntegerPreferenceDefault
import co.electriccoin.zcash.preference.model.entry.PreferenceKey
import kotlinx.coroutines.flow.Flow
interface ShieldFundsRemindMeCountStorageProvider {
suspend fun get(forAccount: AccountUuid): Int
suspend fun store(forAccount: AccountUuid, amount: Int)
suspend fun observe(forAccount: AccountUuid): Flow<Int>
}
class ShieldFundsRemindMeCountStorageProviderImpl(
private val encryptedPreferenceProvider: EncryptedPreferenceProvider
) : ShieldFundsRemindMeCountStorageProvider {
@OptIn(ExperimentalStdlibApi::class)
private fun getDefault(forAccount: AccountUuid): IntegerPreferenceDefault {
val key = PreferenceKey("shield_funds_remind_me_count_${forAccount.value.toHexString()}")
return IntegerPreferenceDefault(key = key, defaultValue = 0)
}
override suspend fun get(forAccount: AccountUuid): Int {
return getDefault(forAccount).getValue(encryptedPreferenceProvider())
}
override suspend fun store(forAccount: AccountUuid, amount: Int) {
getDefault(forAccount).putValue(encryptedPreferenceProvider(), amount)
}
override suspend fun observe(forAccount: AccountUuid): Flow<Int> {
return getDefault(forAccount).observe(encryptedPreferenceProvider())
}
}

View File

@ -0,0 +1,38 @@
package co.electriccoin.zcash.ui.common.provider
import cash.z.ecc.android.sdk.model.AccountUuid
import co.electriccoin.zcash.preference.EncryptedPreferenceProvider
import co.electriccoin.zcash.preference.model.entry.TimestampPreferenceDefault
import co.electriccoin.zcash.preference.model.entry.PreferenceKey
import kotlinx.coroutines.flow.Flow
import java.time.Instant
interface ShieldFundsRemindMeTimestampStorageProvider {
suspend fun get(forAccount: AccountUuid): Instant?
suspend fun store(forAccount: AccountUuid, timestamp: Instant)
suspend fun observe(forAccount: AccountUuid): Flow<Instant?>
}
class ShieldFundsRemindMeTimestampStorageProviderImpl(
private val encryptedPreferenceProvider: EncryptedPreferenceProvider
) : ShieldFundsRemindMeTimestampStorageProvider {
@OptIn(ExperimentalStdlibApi::class)
private fun getDefault(forAccount: AccountUuid): TimestampPreferenceDefault {
val key = PreferenceKey("shield_funds_remind_me_timestamp_${forAccount.value.toHexString()}")
return TimestampPreferenceDefault(key = key)
}
override suspend fun get(forAccount: AccountUuid): Instant? {
return getDefault(forAccount).getValue(encryptedPreferenceProvider())
}
override suspend fun store(forAccount: AccountUuid, timestamp: Instant) {
getDefault(forAccount).putValue(encryptedPreferenceProvider(), timestamp)
}
override suspend fun observe(forAccount: AccountUuid): Flow<Instant?> {
return getDefault(forAccount).observe(encryptedPreferenceProvider())
}
}

View File

@ -53,7 +53,7 @@ class SynchronizerProviderImpl(
}.flowOn(Dispatchers.IO)
.stateIn(
scope = scope,
started = SharingStarted.WhileSubscribed(Duration.ZERO, Duration.ZERO),
started = SharingStarted.Lazily,
initialValue = null
)

View File

@ -0,0 +1,36 @@
package co.electriccoin.zcash.ui.common.provider
import co.electriccoin.zcash.preference.PreferenceHolder
import co.electriccoin.zcash.preference.api.PreferenceProvider
import co.electriccoin.zcash.preference.model.entry.PreferenceKey
import co.electriccoin.zcash.preference.model.entry.TimestampPreferenceDefault
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow
import java.time.Instant
interface TimestampStorageProvider {
suspend fun get(): Instant?
suspend fun store(timestamp: Instant)
fun observe(): Flow<Instant?>
suspend fun clear()
}
abstract class BaseTimestampStorageProvider(key: PreferenceKey) : TimestampStorageProvider {
protected abstract val preferenceHolder: PreferenceHolder
private val default = TimestampPreferenceDefault(key)
private suspend fun getPreferenceProvider(): PreferenceProvider = preferenceHolder()
override suspend fun get(): Instant? = default.getValue(getPreferenceProvider())
override suspend fun store(timestamp: Instant) = default.putValue(getPreferenceProvider(), timestamp)
override fun observe(): Flow<Instant?> = flow { emitAll(default.observe(getPreferenceProvider())) }
override suspend fun clear() = default.putValue(getPreferenceProvider(), null)
}

View File

@ -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 WalletBackupFlagStorageProvider : BooleanStorageProvider
class WalletBackupFlagStorageProviderImpl(
override val preferenceHolder: EncryptedPreferenceProvider
) : BaseBooleanStorageProvider(key = PreferenceKey("wallet_backup_flag")),
WalletBackupFlagStorageProvider

View File

@ -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 WalletBackupRemindMeCountStorageProvider : IntStorageProvider
class WalletBackupRemindMeCountStorageProviderImpl(
override val preferenceHolder: EncryptedPreferenceProvider
) : BaseIntStorageProvider(key = PreferenceKey("wallet_backup_remind_me_count")),
WalletBackupRemindMeCountStorageProvider

View File

@ -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 WalletBackupRemindMeTimestampStorageProvider : TimestampStorageProvider
class WalletBackupRemindMeTimestampStorageProviderImpl(
override val preferenceHolder: EncryptedPreferenceProvider
) : BaseTimestampStorageProvider(key = PreferenceKey("wallet_backup_remind_me_timestamp")),
WalletBackupRemindMeTimestampStorageProvider

View File

@ -5,13 +5,11 @@ import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
import co.electriccoin.zcash.preference.StandardPreferenceProvider
import co.electriccoin.zcash.preference.model.entry.NullableBooleanPreferenceDefault
import co.electriccoin.zcash.spackle.Twig
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.common.provider.SynchronizerProvider
import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState
import co.electriccoin.zcash.ui.common.wallet.RefreshLock
import co.electriccoin.zcash.ui.common.wallet.StaleLock
import co.electriccoin.zcash.ui.preference.StandardPreferenceKeys
import co.electriccoin.zcash.ui.screen.exchangerate.optin.ExchangeRateOptIn
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -37,26 +35,19 @@ import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
interface ExchangeRateRepository {
val isExchangeRateUsdOptedIn: StateFlow<Boolean?>
val state: StateFlow<ExchangeRateState>
fun optInExchangeRateUsd(optIn: Boolean)
fun dismissOptInExchangeRateUsd()
fun refreshExchangeRateUsd()
}
class ExchangeRateRepositoryImpl(
private val synchronizerProvider: SynchronizerProvider,
private val standardPreferenceProvider: StandardPreferenceProvider,
private val navigationRouter: NavigationRouter,
) : ExchangeRateRepository {
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
override val isExchangeRateUsdOptedIn: StateFlow<Boolean?> =
nullableBooleanStateFlow(StandardPreferenceKeys.EXCHANGE_RATE_OPTED_IN)
private val isExchangeRateUsdOptedIn = nullableBooleanStateFlow(StandardPreferenceKeys.EXCHANGE_RATE_OPTED_IN)
@OptIn(ExperimentalCoroutinesApi::class)
private val exchangeRateUsdInternal =
@ -167,17 +158,13 @@ class ExchangeRateRepositoryImpl(
}
false -> ExchangeRateState.OptedOut
null ->
ExchangeRateState.OptIn(
onDismissClick = ::dismissWidgetOptInExchangeRateUsd,
onPrimaryClick = ::showOptInExchangeRateUsd
)
null -> ExchangeRateState.OptIn
}
return lastExchangeRateUsdValue
}
override fun refreshExchangeRateUsd() {
private fun refreshExchangeRateUsd() {
refreshExchangeRateUsdInternal()
}
@ -192,20 +179,8 @@ class ExchangeRateRepositoryImpl(
override fun optInExchangeRateUsd(optIn: Boolean) {
setNullableBooleanPreference(StandardPreferenceKeys.EXCHANGE_RATE_OPTED_IN, optIn)
navigationRouter.back()
}
override fun dismissOptInExchangeRateUsd() {
setNullableBooleanPreference(StandardPreferenceKeys.EXCHANGE_RATE_OPTED_IN, false)
navigationRouter.back()
}
private fun dismissWidgetOptInExchangeRateUsd() {
setNullableBooleanPreference(StandardPreferenceKeys.EXCHANGE_RATE_OPTED_IN, false)
}
private fun showOptInExchangeRateUsd() = navigationRouter.forward(ExchangeRateOptIn)
private fun nullableBooleanStateFlow(default: NullableBooleanPreferenceDefault): StateFlow<Boolean?> =
flow {
emitAll(default.observe(standardPreferenceProvider()))
@ -217,7 +192,7 @@ class ExchangeRateRepositoryImpl(
private fun setNullableBooleanPreference(
default: NullableBooleanPreferenceDefault,
newState: Boolean
newState: Boolean?
) {
scope.launch {
default.putValue(standardPreferenceProvider(), newState)

View File

@ -20,7 +20,6 @@ import co.electriccoin.zcash.ui.common.datasource.AccountDataSource
import co.electriccoin.zcash.ui.common.datasource.RestoreTimestampDataSource
import co.electriccoin.zcash.ui.common.model.FastestServersState
import co.electriccoin.zcash.ui.common.model.OnboardingState
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.common.model.WalletAccount
import co.electriccoin.zcash.ui.common.model.WalletRestoringState
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
@ -81,11 +80,6 @@ interface WalletRepository {
*/
val walletRestoringState: StateFlow<WalletRestoringState>
/**
* A flow of the wallet current state information that should be displayed in screens top app bar.
*/
val walletStateInformation: StateFlow<TopAppBarSubTitleState>
fun persistWallet(persistableWallet: PersistableWallet)
fun persistOnboardingState(onboardingState: OnboardingState)
@ -228,29 +222,6 @@ class WalletRepositoryImpl(
initialValue = WalletRestoringState.NONE
)
@OptIn(ExperimentalCoroutinesApi::class)
override val walletStateInformation: StateFlow<TopAppBarSubTitleState> =
synchronizer
.filterNotNull()
.flatMapLatest { synchronizer ->
combine(
synchronizer.status,
walletRestoringState
) { status: Synchronizer.Status?, walletRestoringState: WalletRestoringState ->
if (Synchronizer.Status.DISCONNECTED == status) {
TopAppBarSubTitleState.Disconnected
} else if (WalletRestoringState.RESTORING == walletRestoringState) {
TopAppBarSubTitleState.Restoring
} else {
TopAppBarSubTitleState.None
}
}
}.stateIn(
scope = scope,
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
initialValue = TopAppBarSubTitleState.None
)
/**
* Persists a wallet asynchronously. Clients observe [secretState] to see the side effects.
*/

View File

@ -0,0 +1,87 @@
package co.electriccoin.zcash.ui.common.usecase
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.model.Zatoshi
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.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.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
class GetHomeMessageUseCase(
private val walletRepository: WalletRepository,
private val walletBackupDataSource: WalletBackupDataSource,
private val exchangeRateRepository: ExchangeRateRepository,
) {
@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class)
fun observe() = 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) ->
when {
walletSnapshot.synchronizerError != null -> {
flowOf(HomeMessageData.Error(walletSnapshot.synchronizerError))
}
walletSnapshot.status == Synchronizer.Status.DISCONNECTED -> {
flowOf(HomeMessageData.Disconnected)
}
walletSnapshot.status in listOf(
Synchronizer.Status.INITIALIZING,
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)
}
}
emit(result)
}
}
backup is WalletBackupAvailability.Available -> flowOf(HomeMessageData.Backup)
isCCAvailable -> flowOf(HomeMessageData.EnableCurrencyConversion)
else -> flowOf(null)
}
}.debounce(.5.seconds)
}
sealed interface HomeMessageData {
data object EnableCurrencyConversion : HomeMessageData
data class TransparentBalance(val zatoshi: Zatoshi) : HomeMessageData
data object Backup : HomeMessageData
data object Disconnected : HomeMessageData
data class Error(val synchronizerError: SynchronizerError) : HomeMessageData
data class Restoring(val progress: Float) : HomeMessageData
data class Syncing(val progress: Float) : HomeMessageData
data object Updating : HomeMessageData
}

View File

@ -1,9 +0,0 @@
package co.electriccoin.zcash.ui.common.usecase
import co.electriccoin.zcash.ui.common.repository.WalletRepository
class GetWalletStateInformationUseCase(
private val walletRepository: WalletRepository
) {
fun observe() = walletRepository.walletStateInformation
}

View File

@ -7,13 +7,13 @@ import co.electriccoin.zcash.ui.common.repository.BiometricRequest
import co.electriccoin.zcash.ui.common.repository.BiometricsCancelledException
import co.electriccoin.zcash.ui.common.repository.BiometricsFailureException
import co.electriccoin.zcash.ui.design.util.stringRes
import co.electriccoin.zcash.ui.screen.seed.SeedRecovery
import co.electriccoin.zcash.ui.screen.walletbackup.WalletBackup
class NavigateToSeedRecoveryUseCase(
class NavigateToWalletBackupUseCase(
private val navigationRouter: NavigationRouter,
private val biometricRepository: BiometricRepository
) {
suspend operator fun invoke() {
suspend operator fun invoke(isOpenedFromSeedBackupInfo: Boolean) {
try {
biometricRepository.requestBiometrics(
BiometricRequest(
@ -24,7 +24,7 @@ class NavigateToSeedRecoveryUseCase(
)
)
)
navigationRouter.forward(SeedRecovery)
navigationRouter.forward(WalletBackup(isOpenedFromSeedBackupInfo = isOpenedFromSeedBackupInfo))
} catch (_: BiometricsFailureException) {
// do nothing
} catch (_: BiometricsCancelledException) {

View File

@ -0,0 +1,14 @@
package co.electriccoin.zcash.ui.common.usecase
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.common.datasource.WalletBackupDataSource
class OnUserSavedWalletBackupUseCase(
private val navigationRouter: NavigationRouter,
private val walletBackupDataSource: WalletBackupDataSource
) {
suspend operator fun invoke() {
walletBackupDataSource.remindMeLater()
navigationRouter.backToRoot()
}
}

View File

@ -0,0 +1,14 @@
package co.electriccoin.zcash.ui.common.usecase
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.common.datasource.WalletBackupDataSource
class RemindWalletBackupLaterUseCase(
private val navigationRouter: NavigationRouter,
private val walletBackupDataSource: WalletBackupDataSource
) {
suspend operator fun invoke() {
walletBackupDataSource.remindMeLater()
navigationRouter.backToRoot()
}
}

View File

@ -3,7 +3,6 @@ package co.electriccoin.zcash.ui.common.viewmodel
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.WalletCoordinator
import cash.z.ecc.android.sdk.WalletInitMode
import cash.z.ecc.android.sdk.model.BlockHeight
@ -13,12 +12,10 @@ import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.sdk.type.fromResources
import co.electriccoin.zcash.preference.StandardPreferenceProvider
import co.electriccoin.zcash.spackle.Twig
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.common.model.OnboardingState
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.repository.ExchangeRateRepository
import co.electriccoin.zcash.ui.common.repository.WalletRepository
import co.electriccoin.zcash.ui.common.usecase.GetSynchronizerUseCase
import co.electriccoin.zcash.ui.common.usecase.IsFlexaAvailableUseCase
@ -44,39 +41,19 @@ class WalletViewModel(
application: Application,
private val walletCoordinator: WalletCoordinator,
private val walletRepository: WalletRepository,
private val exchangeRateRepository: ExchangeRateRepository,
private val standardPreferenceProvider: StandardPreferenceProvider,
private val getAvailableServers: GetDefaultServersProvider,
private val resetInMemoryData: ResetInMemoryDataUseCase,
private val resetSharedPrefsData: ResetSharedPrefsDataUseCase,
private val isFlexaAvailable: IsFlexaAvailableUseCase,
private val getSynchronizer: GetSynchronizerUseCase,
private val navigationRouter: NavigationRouter,
) : AndroidViewModel(application) {
val synchronizer = walletRepository.synchronizer
val walletStateInformation = walletRepository.walletStateInformation
val secretState: StateFlow<SecretState> = walletRepository.secretState
val currentWalletSnapshot: StateFlow<WalletSnapshot?> = walletRepository.currentWalletSnapshot
val isExchangeRateUsdOptedIn = exchangeRateRepository.isExchangeRateUsdOptedIn
val exchangeRateUsd = exchangeRateRepository.state
fun optInExchangeRateUsd(optIn: Boolean) {
exchangeRateRepository.optInExchangeRateUsd(optIn)
}
fun dismissOptInExchangeRateUsd() {
navigationRouter.back()
}
fun onSkipClick() {
navigationRouter.back()
}
fun persistNewWalletAndRestoringState(state: WalletRestoringState) {
val application = getApplication<Application>()
@ -259,5 +236,3 @@ sealed class SynchronizerError {
override fun getStackTrace(limit: Int?): String? = null
}
}
fun Synchronizer.Status.isSynced() = this == Synchronizer.Status.SYNCED

View File

@ -13,10 +13,7 @@ sealed interface ExchangeRateState {
val onRefresh: () -> Unit,
) : ExchangeRateState
data class OptIn(
val onDismissClick: () -> Unit = {},
val onPrimaryClick: () -> Unit = {}
) : ExchangeRateState
data object OptIn : ExchangeRateState
data object OptedOut : ExchangeRateState
}

View File

@ -26,7 +26,6 @@ object ZashiMainTopAppBarStateFixture {
) = ZashiMainTopAppBarState(
accountSwitchState = accountSwitchState,
balanceVisibilityButton = balanceVisibilityButton,
settingsButton = settingsButton,
subtitle = null
settingsButton = settingsButton
)
}

View File

@ -16,4 +16,8 @@ data class PersistableWalletPreferenceDefault(
preferenceProvider: PreferenceProvider,
newValue: PersistableWallet?
) = preferenceProvider.putString(key, newValue?.toJson()?.toString())
suspend fun remove(preferenceProvider: PreferenceProvider) {
preferenceProvider.remove(key)
}
}

View File

@ -9,13 +9,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.configuration.api.ConfigurationProvider
import co.electriccoin.zcash.di.koinActivityViewModel
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.screen.about.util.WebBrowserUtil
import co.electriccoin.zcash.ui.screen.about.view.About
import co.electriccoin.zcash.ui.screen.support.model.ConfigInfo
@ -26,9 +23,6 @@ import org.koin.compose.koinInject
@Composable
internal fun WrapAbout(goBack: () -> Unit) {
val activity = LocalActivity.current
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
BackHandler {
goBack()
@ -49,7 +43,6 @@ internal fun WrapAbout(goBack: () -> Unit) {
About(
onBack = goBack,
versionInfo = versionInfo,
configInfo = configInfo,
onPrivacyPolicy = {
openPrivacyPolicyInWebBrowser(
@ -59,7 +52,7 @@ internal fun WrapAbout(goBack: () -> Unit) {
)
},
snackbarHostState = snackbarHostState,
topAppBarSubTitleState = walletState,
versionInfo = versionInfo,
)
}

View File

@ -29,7 +29,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.design.component.ZashiSmallTopAppBar
import co.electriccoin.zcash.ui.design.component.ZashiTopAppBarBackNavigation
@ -48,13 +47,11 @@ import co.electriccoin.zcash.ui.fixture.VersionInfoFixture
import co.electriccoin.zcash.ui.screen.support.model.ConfigInfo
@Composable
@Suppress("LongParameterList")
fun About(
onBack: () -> Unit,
configInfo: ConfigInfo,
onPrivacyPolicy: () -> Unit,
snackbarHostState: SnackbarHostState,
topAppBarSubTitleState: TopAppBarSubTitleState,
versionInfo: VersionInfo,
) {
Scaffold(
@ -63,7 +60,6 @@ fun About(
onBack = onBack,
versionInfo = versionInfo,
configInfo = configInfo,
subTitleState = topAppBarSubTitleState,
)
},
snackbarHost = { SnackbarHost(snackbarHostState) },
@ -85,16 +81,9 @@ fun About(
private fun AboutTopAppBar(
onBack: () -> Unit,
versionInfo: VersionInfo,
configInfo: ConfigInfo,
subTitleState: TopAppBarSubTitleState
configInfo: ConfigInfo
) {
ZashiSmallTopAppBar(
subtitle =
when (subTitleState) {
TopAppBarSubTitleState.Disconnected -> stringResource(id = R.string.disconnected_label)
TopAppBarSubTitleState.Restoring -> stringResource(id = R.string.restoring_wallet_label)
TopAppBarSubTitleState.None -> null
},
title = stringResource(id = R.string.about_title),
navigationAction = {
ZashiTopAppBarBackNavigation(onBack = onBack)
@ -198,7 +187,6 @@ private fun AboutPreview() =
configInfo = ConfigInfoFixture.new(),
onPrivacyPolicy = {},
snackbarHostState = SnackbarHostState(),
topAppBarSubTitleState = TopAppBarSubTitleState.None,
versionInfo = VersionInfoFixture.new(),
)
}

View File

@ -6,8 +6,6 @@ import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.di.koinActivityViewModel
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.screen.addressbook.view.AddressBookView
import co.electriccoin.zcash.ui.screen.addressbook.viewmodel.AddressBookViewModel
import co.electriccoin.zcash.ui.screen.addressbook.viewmodel.SelectRecipientViewModel
@ -23,9 +21,7 @@ internal fun WrapAddressBook(args: AddressBookArgs) {
@Composable
private fun WrapAddressBook() {
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val viewModel = koinViewModel<AddressBookViewModel>()
val walletState by walletViewModel.walletStateInformation.collectAsStateWithLifecycle()
val state by viewModel.state.collectAsStateWithLifecycle()
BackHandler {
@ -34,15 +30,12 @@ private fun WrapAddressBook() {
AddressBookView(
state = state,
topAppBarSubTitleState = walletState,
)
}
@Composable
private fun WrapSelectRecipient() {
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val viewModel = koinViewModel<SelectRecipientViewModel>()
val walletState by walletViewModel.walletStateInformation.collectAsStateWithLifecycle()
val state by viewModel.state.collectAsStateWithLifecycle()
BackHandler {
@ -51,7 +44,6 @@ private fun WrapSelectRecipient() {
AddressBookView(
state = state,
topAppBarSubTitleState = walletState,
)
}

View File

@ -40,7 +40,6 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.design.component.BlankBgScaffold
import co.electriccoin.zcash.ui.design.component.ButtonState
import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator
@ -67,14 +66,12 @@ import co.electriccoin.zcash.ui.screen.addressbook.model.AddressBookState
@Composable
fun AddressBookView(
state: AddressBookState,
topAppBarSubTitleState: TopAppBarSubTitleState
state: AddressBookState
) {
BlankBgScaffold(
topBar = {
AddressBookTopAppBar(
onBack = state.onBack,
subTitleState = topAppBarSubTitleState,
state = state
)
}
@ -322,17 +319,10 @@ private fun AddContactButton(
@Composable
private fun AddressBookTopAppBar(
onBack: () -> Unit,
subTitleState: TopAppBarSubTitleState,
state: AddressBookState,
) {
ZashiSmallTopAppBar(
title = state.title.getValue(),
subtitle =
when (subTitleState) {
TopAppBarSubTitleState.Disconnected -> stringResource(id = R.string.disconnected_label)
TopAppBarSubTitleState.Restoring -> stringResource(id = R.string.restoring_wallet_label)
TopAppBarSubTitleState.None -> null
},
modifier = Modifier.testTag(AddressBookTag.TOP_APP_BAR),
showTitleLogo = true,
navigationAction = {
@ -382,7 +372,6 @@ private fun AddressBookDataPreview() {
),
title = stringRes("Address book")
),
topAppBarSubTitleState = TopAppBarSubTitleState.None,
)
}
}
@ -447,7 +436,6 @@ private fun SelectRecipientDataPreview() {
),
title = stringRes("Select Recipient")
),
topAppBarSubTitleState = TopAppBarSubTitleState.None,
)
}
}
@ -472,7 +460,6 @@ private fun LoadingPreview() {
),
title = stringRes("Select Recipient")
),
topAppBarSubTitleState = TopAppBarSubTitleState.None,
)
}
}
@ -497,7 +484,6 @@ private fun EmptyAddressBookPreview() {
),
title = stringRes("Address Book")
),
topAppBarSubTitleState = TopAppBarSubTitleState.None,
)
}
}
@ -535,7 +521,6 @@ private fun EmptySelectRecipientPreview() {
),
title = stringRes("Select Recipient")
),
topAppBarSubTitleState = TopAppBarSubTitleState.None,
)
}
}

View File

@ -24,7 +24,6 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.fastForEachIndexed
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.design.component.BlankBgScaffold
import co.electriccoin.zcash.ui.design.component.ButtonState
import co.electriccoin.zcash.ui.design.component.ZashiButton
@ -47,13 +46,11 @@ import kotlinx.collections.immutable.persistentListOf
@Composable
fun AdvancedSettings(
state: AdvancedSettingsState,
topAppBarSubTitleState: TopAppBarSubTitleState,
) {
BlankBgScaffold(
topBar = {
AdvancedSettingsTopAppBar(
onBack = state.onBack,
subTitleState = topAppBarSubTitleState,
)
}
) { paddingValues ->
@ -114,17 +111,10 @@ private fun Info() {
@Composable
private fun AdvancedSettingsTopAppBar(
onBack: () -> Unit,
subTitleState: TopAppBarSubTitleState
onBack: () -> Unit
) {
ZashiSmallTopAppBar(
title = stringResource(id = R.string.advanced_settings_title),
subtitle =
when (subTitleState) {
TopAppBarSubTitleState.Disconnected -> stringResource(id = R.string.disconnected_label)
TopAppBarSubTitleState.Restoring -> stringResource(id = R.string.restoring_wallet_label)
TopAppBarSubTitleState.None -> null
},
modifier = Modifier.testTag(AdvancedSettingsTag.ADVANCED_SETTINGS_TOP_APP_BAR),
showTitleLogo = true,
navigationAction = {
@ -170,6 +160,5 @@ private fun AdvancedSettingsPreview() =
onClick = {}
)
),
topAppBarSubTitleState = TopAppBarSubTitleState.None,
)
}

View File

@ -8,12 +8,12 @@ import co.electriccoin.zcash.ui.NavigationTargets
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.WalletRestoringState
import co.electriccoin.zcash.ui.common.usecase.GetWalletRestoringStateUseCase
import co.electriccoin.zcash.ui.common.usecase.NavigateToSeedRecoveryUseCase
import co.electriccoin.zcash.ui.common.usecase.NavigateToWalletBackupUseCase
import co.electriccoin.zcash.ui.common.usecase.NavigateToTaxExportUseCase
import co.electriccoin.zcash.ui.design.component.ButtonState
import co.electriccoin.zcash.ui.design.component.listitem.ZashiListItemState
import co.electriccoin.zcash.ui.design.util.stringRes
import co.electriccoin.zcash.ui.screen.exchangerate.optin.ExchangeRateOptIn
import co.electriccoin.zcash.ui.screen.exchangerate.settings.ExchangeRateSettings
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@ -26,7 +26,7 @@ class AdvancedSettingsViewModel(
getWalletRestoringState: GetWalletRestoringStateUseCase,
private val navigationRouter: NavigationRouter,
private val navigateToTaxExport: NavigateToTaxExportUseCase,
private val navigateToSeedRecovery: NavigateToSeedRecoveryUseCase
private val navigateToSeedRecovery: NavigateToWalletBackupUseCase
) : ViewModel() {
val state: StateFlow<AdvancedSettingsState> =
getWalletRestoringState
@ -89,7 +89,7 @@ class AdvancedSettingsViewModel(
private fun onChooseServerClick() = navigationRouter.forward(NavigationTargets.CHOOSE_SERVER)
private fun onCurrencyConversionClick() = navigationRouter.forward(ExchangeRateOptIn)
private fun onCurrencyConversionClick() = navigationRouter.forward(ExchangeRateSettings)
private fun onTaxExportClick() =
viewModelScope.launch {
@ -98,6 +98,6 @@ class AdvancedSettingsViewModel(
private fun onSeedRecoveryClick() =
viewModelScope.launch {
navigateToSeedRecovery()
navigateToSeedRecovery(isOpenedFromSeedBackupInfo = false)
}
}

View File

@ -5,8 +5,6 @@ package co.electriccoin.zcash.ui.screen.advancedsettings
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.di.koinActivityViewModel
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import kotlinx.collections.immutable.toImmutableList
import org.koin.androidx.compose.koinViewModel
@ -15,9 +13,7 @@ internal fun WrapAdvancedSettings(
goDeleteWallet: () -> Unit,
goExportPrivateData: () -> Unit,
) {
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val viewModel = koinViewModel<AdvancedSettingsViewModel>()
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
val originalState = viewModel.state.collectAsStateWithLifecycle().value
val state =
originalState.copy(
@ -38,6 +34,5 @@ internal fun WrapAdvancedSettings(
AdvancedSettings(
state = state,
topAppBarSubTitleState = walletState,
)
}

View File

@ -6,17 +6,13 @@ import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.di.koinActivityViewModel
import co.electriccoin.zcash.ui.common.compose.LocalNavController
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import org.koin.androidx.compose.koinViewModel
@Composable
internal fun WrapChooseServer() {
val navController = LocalNavController.current
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val viewModel = koinViewModel<ChooseServerViewModel>()
val walletState by walletViewModel.walletStateInformation.collectAsStateWithLifecycle()
val state by viewModel.state.collectAsStateWithLifecycle()
BackHandler {
@ -32,6 +28,5 @@ internal fun WrapChooseServer() {
navController.popBackStack()
}
},
topAppBarSubTitleState = walletState,
)
}

View File

@ -46,7 +46,6 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.design.R.drawable
import co.electriccoin.zcash.ui.design.component.AlertDialogState
import co.electriccoin.zcash.ui.design.component.AppAlertDialog
@ -78,7 +77,6 @@ import co.electriccoin.zcash.ui.design.util.stringRes
@Composable
fun ChooseServerView(
state: ChooseServerState?,
topAppBarSubTitleState: TopAppBarSubTitleState,
onBack: () -> Unit,
) {
if (state == null) {
@ -91,7 +89,6 @@ fun ChooseServerView(
topBar = {
ChooseServerTopAppBar(
onBack = onBack,
subTitleState = topAppBarSubTitleState,
)
},
bottomBar = {
@ -208,17 +205,10 @@ fun ChooseServerBottomBar(saveButtonState: ButtonState) {
@Composable
private fun ChooseServerTopAppBar(
onBack: () -> Unit,
subTitleState: TopAppBarSubTitleState
onBack: () -> Unit
) {
ZashiSmallTopAppBar(
title = stringResource(id = R.string.choose_server_title),
subtitle =
when (subTitleState) {
TopAppBarSubTitleState.Disconnected -> stringResource(id = R.string.disconnected_label)
TopAppBarSubTitleState.Restoring -> stringResource(id = R.string.restoring_wallet_label)
TopAppBarSubTitleState.None -> null
},
modifier = Modifier.testTag(CHOOSE_SERVER_TOP_APP_BAR),
showTitleLogo = true,
navigationAction = {
@ -535,7 +525,6 @@ private fun ChooseServerPreview(
dialogState = dialogState
),
onBack = {},
topAppBarSubTitleState = TopAppBarSubTitleState.None,
)
}

View File

@ -6,8 +6,6 @@ import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.di.koinActivityViewModel
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.screen.contact.view.ContactView
import co.electriccoin.zcash.ui.screen.contact.viewmodel.AddContactViewModel
import org.koin.androidx.compose.koinViewModel
@ -15,9 +13,7 @@ import org.koin.core.parameter.parametersOf
@Composable
internal fun WrapAddContact(address: String?) {
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val viewModel = koinViewModel<AddContactViewModel> { parametersOf(address) }
val walletState by walletViewModel.walletStateInformation.collectAsStateWithLifecycle()
val state by viewModel.state.collectAsStateWithLifecycle()
BackHandler {
@ -27,7 +23,6 @@ internal fun WrapAddContact(address: String?) {
state?.let {
ContactView(
state = it,
topAppBarSubTitleState = walletState,
)
}
}

View File

@ -6,8 +6,6 @@ import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.di.koinActivityViewModel
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.screen.contact.view.ContactView
import co.electriccoin.zcash.ui.screen.contact.viewmodel.UpdateContactViewModel
import org.koin.androidx.compose.koinViewModel
@ -15,9 +13,7 @@ import org.koin.core.parameter.parametersOf
@Composable
internal fun WrapUpdateContact(contactAddress: String) {
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val viewModel = koinViewModel<UpdateContactViewModel> { parametersOf(contactAddress) }
val walletState by walletViewModel.walletStateInformation.collectAsStateWithLifecycle()
val state by viewModel.state.collectAsStateWithLifecycle()
BackHandler {
@ -27,7 +23,6 @@ internal fun WrapUpdateContact(contactAddress: String) {
state?.let {
ContactView(
state = it,
topAppBarSubTitleState = walletState,
)
}
}

View File

@ -15,7 +15,6 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.unit.dp
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.design.component.BlankBgScaffold
import co.electriccoin.zcash.ui.design.component.ButtonState
import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator
@ -37,12 +36,11 @@ import co.electriccoin.zcash.ui.screen.contact.model.ContactState
@Composable
fun ContactView(
state: ContactState,
topAppBarSubTitleState: TopAppBarSubTitleState
state: ContactState
) {
BlankBgScaffold(
topBar = {
ContactTopAppBar(onBack = state.onBack, subTitleState = topAppBarSubTitleState, state = state)
ContactTopAppBar(onBack = state.onBack, state = state)
}
) { paddingValues ->
if (state.isLoading) {
@ -129,17 +127,10 @@ private fun ContactViewInternal(
@Composable
private fun ContactTopAppBar(
onBack: () -> Unit,
subTitleState: TopAppBarSubTitleState,
state: ContactState
) {
ZashiSmallTopAppBar(
title = state.title.getValue(),
subtitle =
when (subTitleState) {
TopAppBarSubTitleState.Disconnected -> stringResource(id = R.string.disconnected_label)
TopAppBarSubTitleState.Restoring -> stringResource(id = R.string.restoring_wallet_label)
TopAppBarSubTitleState.None -> null
},
modifier = Modifier.testTag(ContactTag.TOP_APP_BAR),
showTitleLogo = true,
navigationAction = {
@ -169,7 +160,6 @@ private fun DataPreview() {
text = stringRes("Negative"),
)
),
topAppBarSubTitleState = TopAppBarSubTitleState.None,
)
}
}
@ -195,7 +185,6 @@ private fun LoadingPreview() {
text = stringRes("Add New Contact"),
)
),
topAppBarSubTitleState = TopAppBarSubTitleState.None,
)
}
}

View File

@ -11,7 +11,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.di.koinActivityViewModel
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.screen.deletewallet.view.DeleteWallet
import kotlinx.coroutines.launch
@ -22,15 +21,11 @@ internal fun MainActivity.WrapDeleteWallet(
onConfirm: () -> Unit,
) {
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
WrapDeleteWallet(
activity = this,
goBack = goBack,
topAppBarSubTitleState = walletState,
walletViewModel = walletViewModel,
onConfirm = onConfirm
onConfirm = onConfirm,
walletViewModel = walletViewModel
)
}
@ -39,7 +34,6 @@ internal fun WrapDeleteWallet(
activity: Activity,
goBack: () -> Unit,
onConfirm: () -> Unit,
topAppBarSubTitleState: TopAppBarSubTitleState,
walletViewModel: WalletViewModel,
) {
val scope = rememberCoroutineScope()
@ -69,6 +63,5 @@ internal fun WrapDeleteWallet(
}
)
},
topAppBarSubTitleState = topAppBarSubTitleState,
)
}

View File

@ -20,7 +20,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
import co.electriccoin.zcash.ui.design.component.ZashiButton
import co.electriccoin.zcash.ui.design.component.ZashiButtonDefaults
@ -43,7 +42,6 @@ private fun ExportPrivateDataPreview() =
snackbarHostState = SnackbarHostState(),
onBack = {},
onConfirm = {},
topAppBarSubTitleState = TopAppBarSubTitleState.None,
)
}
@ -52,13 +50,11 @@ fun DeleteWallet(
snackbarHostState: SnackbarHostState,
onBack: () -> Unit,
onConfirm: () -> Unit,
topAppBarSubTitleState: TopAppBarSubTitleState,
) {
Scaffold(
topBar = {
DeleteWalletDataTopAppBar(
onBack = onBack,
subTitleState = topAppBarSubTitleState,
)
},
snackbarHost = { SnackbarHost(snackbarHostState) },
@ -76,17 +72,10 @@ fun DeleteWallet(
@Composable
private fun DeleteWalletDataTopAppBar(
onBack: () -> Unit,
subTitleState: TopAppBarSubTitleState
onBack: () -> Unit
) {
ZashiSmallTopAppBar(
title = stringResource(R.string.delete_wallet_title),
subtitle =
when (subTitleState) {
TopAppBarSubTitleState.Disconnected -> stringResource(id = R.string.disconnected_label)
TopAppBarSubTitleState.Restoring -> stringResource(id = R.string.restoring_wallet_label)
TopAppBarSubTitleState.None -> null
},
navigationAction = {
ZashiTopAppBarBackNavigation(
onBack = onBack

View File

@ -1,26 +1,18 @@
package co.electriccoin.zcash.ui.screen.exchangerate.optin
import androidx.activity.compose.BackHandler
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.serialization.Serializable
import org.koin.androidx.compose.koinViewModel
@Composable
fun AndroidExchangeRateOptIn() {
val activity = LocalActivity.current
val walletViewModel by activity.viewModels<WalletViewModel>()
BackHandler {
walletViewModel.dismissOptInExchangeRateUsd()
}
ExchangeRateOptIn(
onEnableClick = { walletViewModel.optInExchangeRateUsd(true) },
onDismiss = { walletViewModel.dismissOptInExchangeRateUsd() },
onSkipClick = { walletViewModel.onSkipClick() }
)
val viewModel = koinViewModel<ExchangeRateOptInViewModel>()
val state by viewModel.state.collectAsStateWithLifecycle()
BackHandler { state.onBack() }
ExchangeRateOptInView(state = state)
}
@Serializable

View File

@ -0,0 +1,7 @@
package co.electriccoin.zcash.ui.screen.exchangerate.optin
data class ExchangeRateOptInState(
val onEnableClick: () -> Unit,
val onBack: () -> Unit,
val onSkipClick: () -> Unit,
)

View File

@ -29,13 +29,9 @@ import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypography
import co.electriccoin.zcash.ui.screen.exchangerate.BaseExchangeRateOptIn
@Composable
fun ExchangeRateOptIn(
onEnableClick: () -> Unit,
onSkipClick: () -> Unit,
onDismiss: () -> Unit,
) {
fun ExchangeRateOptInView(state: ExchangeRateOptInState) {
BaseExchangeRateOptIn(
onDismiss = onDismiss,
onDismiss = state.onBack,
content = {
Spacer(modifier = Modifier.height(8.dp))
Text(
@ -61,7 +57,7 @@ fun ExchangeRateOptIn(
footer = {
ZashiTextButton(
modifier = Modifier.fillMaxWidth(),
onClick = onSkipClick,
onClick = state.onSkipClick,
) {
Text(
text = stringResource(R.string.exchange_rate_opt_in_skip),
@ -72,7 +68,7 @@ fun ExchangeRateOptIn(
ZashiButton(
modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.exchange_rate_opt_in_enable),
onClick = onEnableClick,
onClick = state.onEnableClick,
colors = ZashiButtonDefaults.primaryColors()
)
}
@ -118,6 +114,12 @@ private fun InfoItem(
private fun CurrencyConversionOptInPreview() =
ZcashTheme {
BlankSurface {
ExchangeRateOptIn(onEnableClick = {}, onDismiss = {}, onSkipClick = {})
ExchangeRateOptInView(
state = ExchangeRateOptInState(
onEnableClick = {},
onBack = {},
onSkipClick = {},
)
)
}
}

View File

@ -0,0 +1,34 @@
package co.electriccoin.zcash.ui.screen.exchangerate.optin
import androidx.lifecycle.ViewModel
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.common.repository.ExchangeRateRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
class ExchangeRateOptInViewModel(
private val exchangeRateRepository: ExchangeRateRepository,
private val navigationRouter: NavigationRouter
): ViewModel() {
val state: StateFlow<ExchangeRateOptInState> = MutableStateFlow(
ExchangeRateOptInState(
onBack = ::dismissOptInExchangeRateUsd,
onEnableClick = ::optInExchangeRateUsd,
onSkipClick = ::onSkipClick
)
)
private fun onSkipClick() {
exchangeRateRepository.optInExchangeRateUsd(false)
navigationRouter.back()
}
private fun optInExchangeRateUsd() {
exchangeRateRepository.optInExchangeRateUsd(true)
navigationRouter.back()
}
private fun dismissOptInExchangeRateUsd() {
navigationRouter.back()
}
}

View File

@ -1,30 +1,18 @@
package co.electriccoin.zcash.ui.screen.exchangerate.settings
import androidx.activity.compose.BackHandler
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.compose.LocalNavController
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import kotlinx.serialization.Serializable
import org.koin.androidx.compose.koinViewModel
@Composable
fun AndroidExchangeRateSettings() {
val activity = LocalActivity.current
val navController = LocalNavController.current
val walletViewModel by activity.viewModels<WalletViewModel>()
val isOptedIn = walletViewModel.isExchangeRateUsdOptedIn.collectAsStateWithLifecycle().value ?: false
BackHandler {
navController.popBackStack()
}
ExchangeRateSettings(
isOptedIn = isOptedIn,
onSaveClick = { walletViewModel.optInExchangeRateUsd(it) },
onDismiss = { navController.popBackStack() }
)
val viewModel = koinViewModel<ExchangeRateSettingsViewModel>()
val state by viewModel.state.collectAsStateWithLifecycle()
BackHandler { state.onDismiss() }
ExchangeRateSettingsView(state = state)
}
@Serializable

View File

@ -0,0 +1,10 @@
package co.electriccoin.zcash.ui.screen.exchangerate.settings
import androidx.compose.runtime.Immutable
@Immutable
data class ExchangeRateSettingsState(
val isOptedIn: Boolean,
val onSaveClick: (optIn: Boolean) -> Unit,
val onDismiss: () -> Unit,
)

View File

@ -37,21 +37,17 @@ import co.electriccoin.zcash.ui.screen.exchangerate.BaseExchangeRateOptIn
import co.electriccoin.zcash.ui.screen.exchangerate.SecondaryCard
@Composable
fun ExchangeRateSettings(
isOptedIn: Boolean,
onDismiss: () -> Unit,
onSaveClick: (Boolean) -> Unit
) {
var isOptInSelected by remember(isOptedIn) { mutableStateOf(isOptedIn) }
fun ExchangeRateSettingsView(state: ExchangeRateSettingsState) {
var isOptInSelected by remember(state.isOptedIn) { mutableStateOf(state.isOptedIn) }
val isButtonDisabled by remember {
derivedStateOf {
(isOptedIn && isOptInSelected) || (!isOptedIn && !isOptInSelected)
(state.isOptedIn && isOptInSelected) || (!state.isOptedIn && !isOptInSelected)
}
}
BaseExchangeRateOptIn(
onDismiss = onDismiss,
onDismiss = state.onDismiss,
content = {
Spacer(modifier = Modifier.height(8.dp))
Text(
@ -82,7 +78,7 @@ fun ExchangeRateSettings(
ZashiButton(
text = stringResource(R.string.exchange_rate_opt_in_save),
modifier = Modifier.fillMaxWidth(),
onClick = { onSaveClick(isOptInSelected) },
onClick = { state.onSaveClick(isOptInSelected) },
enabled = !isButtonDisabled,
colors = ZashiButtonDefaults.primaryColors()
)
@ -187,6 +183,12 @@ private val Unchecked: Int
private fun SettingsExchangeRateOptInPreview() =
ZcashTheme {
BlankSurface {
ExchangeRateSettings(isOptedIn = true, onDismiss = {}, onSaveClick = {})
ExchangeRateSettingsView(
state = ExchangeRateSettingsState(
isOptedIn = true,
onSaveClick = {},
onDismiss = {}
)
)
}
}

View File

@ -0,0 +1,44 @@
package co.electriccoin.zcash.ui.screen.exchangerate.settings
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.common.repository.ExchangeRateRepository
import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.WhileSubscribed
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
class ExchangeRateSettingsViewModel(
private val exchangeRateRepository: ExchangeRateRepository,
private val navigationRouter: NavigationRouter,
) : ViewModel() {
val state = exchangeRateRepository.state
.map {
createState(it)
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
initialValue = createState(exchangeRateRepository.state.value)
)
private fun createState(it: ExchangeRateState) =
ExchangeRateSettingsState(
isOptedIn = it is ExchangeRateState.OptIn,
onSaveClick = ::onOptInExchangeRateUsdClick,
onDismiss = ::onBack
)
private fun onBack() {
navigationRouter.back()
}
private fun onOptInExchangeRateUsdClick(optInt: Boolean) {
exchangeRateRepository.optInExchangeRateUsd(optIn = optInt)
navigationRouter.back()
}
}

View File

@ -1,126 +0,0 @@
package co.electriccoin.zcash.ui.screen.exchangerate.widget
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState
import co.electriccoin.zcash.ui.design.component.BlankSurface
import co.electriccoin.zcash.ui.design.component.ZashiButton
import co.electriccoin.zcash.ui.design.component.ZashiButtonDefaults
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.screen.exchangerate.SecondaryCard
@Suppress("LongMethod")
@Composable
fun StyledExchangeOptIn(
state: ExchangeRateState.OptIn,
modifier: Modifier = Modifier
) {
SecondaryCard(
modifier = modifier,
) {
Column(
modifier = Modifier.padding(start = 20.dp, bottom = 20.dp)
) {
Row {
Image(
modifier = Modifier.padding(top = 20.dp),
painter = painterResource(Icon),
contentDescription = null
)
Spacer(modifier = Modifier.width(12.dp))
Column(
verticalArrangement = Arrangement.Center
) {
Spacer(modifier = Modifier.height(22.dp))
Text(
text = stringResource(R.string.exchange_rate_opt_in_title),
color = ZashiColors.Text.textTertiary,
fontSize = 14.sp,
)
Spacer(modifier = Modifier.height(2.dp))
Text(
text = stringResource(R.string.exchange_rate_opt_in_subtitle),
color = ZashiColors.Text.textPrimary,
fontSize = 16.sp,
style = ZcashTheme.extendedTypography.restoringTopAppBarStyle,
)
}
Spacer(modifier = Modifier.weight(1f))
IconButton(
modifier = Modifier.padding(top = 4.dp, end = 8.dp),
onClick = state.onDismissClick,
) {
Icon(
painter = painterResource(R.drawable.ic_exchange_rate_unavailable_dialog_close),
contentDescription = null,
tint = ZashiColors.HintTooltips.defaultFg
)
}
}
Spacer(modifier = Modifier.height(16.dp))
ZashiButton(
modifier =
Modifier
.fillMaxWidth()
.padding(end = 20.dp),
onClick = state.onPrimaryClick,
colors = ZashiButtonDefaults.tertiaryColors(),
text = stringResource(R.string.exchange_rate_opt_in_primary_btn),
) { scope ->
Text(
text = stringResource(R.string.exchange_rate_opt_in_primary_btn),
style =
ZcashTheme.typography.primary.titleSmall
.copy(fontWeight = FontWeight.SemiBold),
fontSize = 14.sp
)
scope.Loading()
}
}
}
}
private val Icon: Int
@DrawableRes
@Composable
get() =
if (isSystemInDarkTheme()) {
R.drawable.ic_exchange_rate_opt_in
} else {
R.drawable.ic_exchange_rate_opt_in_light
}
@Suppress("UnusedPrivateMember")
@Composable
@PreviewScreens
private fun ExchangeRateOptInPreview() =
ZcashTheme {
BlankSurface {
StyledExchangeOptIn(
modifier = Modifier.fillMaxWidth(),
state = ExchangeRateState.OptIn(onDismissClick = {})
)
}
}

View File

@ -14,7 +14,6 @@ import cash.z.ecc.sdk.type.fromResources
import co.electriccoin.zcash.di.koinActivityViewModel
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator
@ -34,13 +33,10 @@ internal fun WrapExportPrivateData(
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
WrapExportPrivateData(
goBack = goBack,
onShare = onConfirm,
synchronizer = synchronizer,
topAppBarSubTitleState = walletState,
)
}
@ -49,7 +45,6 @@ internal fun WrapExportPrivateData(
goBack: () -> Unit,
onShare: () -> Unit,
synchronizer: Synchronizer?,
topAppBarSubTitleState: TopAppBarSubTitleState,
) {
val activity = LocalActivity.current
@ -85,7 +80,6 @@ internal fun WrapExportPrivateData(
}
}
},
topAppBarSubTitleState = topAppBarSubTitleState,
)
}
}

View File

@ -19,7 +19,6 @@ import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.design.component.ZashiButton
import co.electriccoin.zcash.ui.design.component.ZashiCheckbox
import co.electriccoin.zcash.ui.design.component.ZashiSmallTopAppBar
@ -38,13 +37,11 @@ fun ExportPrivateData(
onBack: () -> Unit,
onAgree: (Boolean) -> Unit,
onConfirm: () -> Unit,
topAppBarSubTitleState: TopAppBarSubTitleState,
) {
Scaffold(
topBar = {
ExportPrivateDataTopAppBar(
onBack = onBack,
subTitleState = topAppBarSubTitleState,
)
},
snackbarHost = { SnackbarHost(snackbarHostState) },
@ -63,17 +60,10 @@ fun ExportPrivateData(
@Composable
private fun ExportPrivateDataTopAppBar(
onBack: () -> Unit,
subTitleState: TopAppBarSubTitleState
onBack: () -> Unit
) {
ZashiSmallTopAppBar(
title = stringResource(R.string.export_data_title),
subtitle =
when (subTitleState) {
TopAppBarSubTitleState.Disconnected -> stringResource(id = R.string.disconnected_label)
TopAppBarSubTitleState.Restoring -> stringResource(id = R.string.restoring_wallet_label)
TopAppBarSubTitleState.None -> null
},
navigationAction = {
ZashiTopAppBarBackNavigation(onBack = onBack)
},
@ -137,6 +127,5 @@ private fun ExportPrivateDataPreview() =
onBack = {},
onAgree = {},
onConfirm = {},
topAppBarSubTitleState = TopAppBarSubTitleState.None,
)
}

View File

@ -7,9 +7,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.di.koinActivityViewModel
import co.electriccoin.zcash.ui.common.compose.LocalNavController
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.design.component.AppAlertDialog
import co.electriccoin.zcash.ui.screen.feedback.view.FeedbackView
import co.electriccoin.zcash.ui.screen.feedback.viewmodel.FeedbackViewModel
@ -18,33 +16,15 @@ import org.koin.androidx.compose.koinViewModel
@Composable
internal fun WrapFeedback() {
val navController = LocalNavController.current
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val viewModel = koinViewModel<FeedbackViewModel>()
val walletState by walletViewModel.walletStateInformation.collectAsStateWithLifecycle()
val state by viewModel.state.collectAsStateWithLifecycle()
val dialogState by viewModel.dialogState.collectAsStateWithLifecycle()
LaunchedEffect(Unit) {
viewModel.onBackNavigationCommand.collect {
navController.popBackStack()
}
}
BackHandler {
state?.onBack?.invoke()
}
state?.let {
FeedbackView(
state = it,
topAppBarSubTitleState = walletState
)
}
dialogState?.let {
AppAlertDialog(
state = it
)
}
BackHandler(enabled = state != null) { state?.onBack?.invoke() }
state?.let { FeedbackView(state = it) }
dialogState?.let { AppAlertDialog(state = it) }
}

View File

@ -34,7 +34,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.design.component.BlankSurface
import co.electriccoin.zcash.ui.design.component.ButtonState
import co.electriccoin.zcash.ui.design.component.TextFieldState
@ -56,13 +55,11 @@ import co.electriccoin.zcash.ui.screen.feedback.model.FeedbackState
@Composable
fun FeedbackView(
state: FeedbackState,
topAppBarSubTitleState: TopAppBarSubTitleState,
) {
Scaffold(
topBar = {
SupportTopAppBar(
state = state,
subTitleState = topAppBarSubTitleState,
)
},
) { paddingValues ->
@ -75,16 +72,9 @@ fun FeedbackView(
@Composable
private fun SupportTopAppBar(
state: FeedbackState,
subTitleState: TopAppBarSubTitleState
state: FeedbackState
) {
ZashiSmallTopAppBar(
subtitle =
when (subTitleState) {
TopAppBarSubTitleState.Disconnected -> stringResource(id = R.string.disconnected_label)
TopAppBarSubTitleState.Restoring -> stringResource(id = R.string.restoring_wallet_label)
TopAppBarSubTitleState.None -> null
},
title = stringResource(id = R.string.support_header),
navigationAction = {
ZashiTopAppBarBackNavigation(onBack = state.onBack)
@ -258,7 +248,6 @@ private fun Preview() =
onSelected = {}
)
),
topAppBarSubTitleState = TopAppBarSubTitleState.None,
)
}
}

View File

@ -11,13 +11,14 @@ import co.electriccoin.zcash.ui.screen.balances.BalanceViewModel
import co.electriccoin.zcash.ui.screen.restoresuccess.WrapRestoreSuccess
import co.electriccoin.zcash.ui.screen.transactionhistory.widget.TransactionHistoryWidgetViewModel
import kotlinx.serialization.Serializable
import org.koin.androidx.compose.koinViewModel
@Composable
internal fun AndroidHome() {
val topAppBarViewModel = koinActivityViewModel<ZashiTopAppBarViewModel>()
val balanceViewModel = koinActivityViewModel<BalanceViewModel>()
val homeViewModel = koinActivityViewModel<HomeViewModel>()
val transactionHistoryWidgetViewModel = koinActivityViewModel<TransactionHistoryWidgetViewModel>()
val balanceViewModel = koinViewModel<BalanceViewModel>()
val homeViewModel = koinViewModel<HomeViewModel>()
val transactionHistoryWidgetViewModel = koinViewModel<TransactionHistoryWidgetViewModel>()
val restoreDialogState by homeViewModel.restoreDialogState.collectAsStateWithLifecycle()
val appBarState by topAppBarViewModel.state.collectAsStateWithLifecycle()
val balanceState by balanceViewModel.state.collectAsStateWithLifecycle()

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.home.messages
package co.electriccoin.zcash.ui.screen.home
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.CubicBezierEasing
@ -35,6 +35,22 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors
import co.electriccoin.zcash.ui.screen.home.backup.WalletBackupMessage
import co.electriccoin.zcash.ui.screen.home.backup.WalletBackupMessageState
import co.electriccoin.zcash.ui.screen.home.currency.EnableCurrencyConversionMessage
import co.electriccoin.zcash.ui.screen.home.currency.EnableCurrencyConversionMessageState
import co.electriccoin.zcash.ui.screen.home.disconnected.WalletDisconnectedMessage
import co.electriccoin.zcash.ui.screen.home.disconnected.WalletDisconnectedMessageState
import co.electriccoin.zcash.ui.screen.home.error.WalletErrorMessage
import co.electriccoin.zcash.ui.screen.home.error.WalletErrorMessageState
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.updating.WalletUpdatingMessage
import co.electriccoin.zcash.ui.screen.home.updating.WalletUpdatingMessageState
import kotlinx.coroutines.delay
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
@ -112,10 +128,10 @@ fun HomeMessage(
contentPadding = contentPadding
)
is TransparentBalanceDetectedMessageState ->
TransparentBalanceDetectedMessage(
is TransparentBalanceMessageState ->
TransparentBalanceMessage(
innerModifier = innerModifier,
state = normalizedState as TransparentBalanceDetectedMessageState,
state = normalizedState as TransparentBalanceMessageState,
contentPadding = contentPadding
)
@ -203,7 +219,7 @@ fun HomeMessage(
}
}
sealed interface HomeMessageState
interface HomeMessageState
@Suppress("MagicNumber")
@Composable

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.home.messages
package co.electriccoin.zcash.ui.screen.home
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@ -24,8 +24,6 @@ import co.electriccoin.zcash.ui.design.component.LocalZashiCircularProgressIndic
import co.electriccoin.zcash.ui.design.component.VerticalSpacer
import co.electriccoin.zcash.ui.design.component.ZashiButtonDefaults
import co.electriccoin.zcash.ui.design.component.ZashiCircularProgressIndicatorDefaults
import co.electriccoin.zcash.ui.design.theme.colors.Purple
import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors
import co.electriccoin.zcash.ui.design.theme.colors.ZashiDarkColors
import co.electriccoin.zcash.ui.design.theme.colors.ZashiLightColors
import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypography
@ -47,11 +45,11 @@ fun HomeMessageWrapper(
innerModifier = innerModifier,
) {
CompositionLocalProvider(
LocalContentColor provides ZashiColors.Text.textOpposite,
LocalContentColor provides ZashiLightColors.Utility.Purple.utilityPurple50,
LocalZashiCircularProgressIndicatorColors provides
ZashiCircularProgressIndicatorDefaults.colors(
progressColor = ZashiColors.Text.textOpposite,
trackColor = Purple.`400`
progressColor = ZashiLightColors.Utility.Purple.utilityPurple50,
trackColor = ZashiLightColors.Utility.Purple.utilityPurple400
)
) {
start()
@ -63,7 +61,7 @@ fun HomeMessageWrapper(
CompositionLocalProvider(
LocalTextStyle provides
ZashiTypography.textSm.copy(
color = ZashiColors.Text.textOpposite,
color = ZashiLightColors.Utility.Purple.utilityPurple50,
fontWeight = FontWeight.Medium
),
) {
@ -73,7 +71,7 @@ fun HomeMessageWrapper(
CompositionLocalProvider(
LocalTextStyle provides
ZashiTypography.textXs.copy(
color = Purple.`200`,
color = ZashiLightColors.Utility.Purple.utilityPurple200,
fontWeight = FontWeight.Medium
),
) {
@ -106,10 +104,11 @@ private fun Container(
Modifier
.background(
Brush.verticalGradient(
0f to Purple.`500`,
1f to Purple.`900`,
0f to ZashiLightColors.Utility.Purple.utilityPurple500,
1f to ZashiLightColors.Utility.Purple.utilityPurple900,
)
).clickable(onClick = onClick)
)
.clickable(onClick = onClick)
.padding(contentPadding),
) {
Row(

View File

@ -1,7 +1,6 @@
package co.electriccoin.zcash.ui.screen.home
import co.electriccoin.zcash.ui.design.component.BigIconButtonState
import co.electriccoin.zcash.ui.screen.home.messages.HomeMessageState
data class HomeState(
val firstButton: BigIconButtonState,
@ -11,6 +10,6 @@ data class HomeState(
val message: HomeMessageState?
)
data class HomeRestoreDialogState(
data class HomeRestoreSuccessDialogState(
val onClick: () -> Unit
)

View File

@ -32,8 +32,7 @@ import co.electriccoin.zcash.ui.fixture.BalanceStateFixture
import co.electriccoin.zcash.ui.fixture.ZashiMainTopAppBarStateFixture
import co.electriccoin.zcash.ui.screen.balances.BalanceState
import co.electriccoin.zcash.ui.screen.balances.BalanceWidget
import co.electriccoin.zcash.ui.screen.home.messages.HomeMessage
import co.electriccoin.zcash.ui.screen.home.messages.WalletErrorMessageState
import co.electriccoin.zcash.ui.screen.home.error.WalletErrorMessageState
import co.electriccoin.zcash.ui.screen.transactionhistory.widget.TransactionHistoryWidgetState
import co.electriccoin.zcash.ui.screen.transactionhistory.widget.TransactionHistoryWidgetStateFixture
import co.electriccoin.zcash.ui.screen.transactionhistory.widget.createTransactionHistoryWidgets

View File

@ -2,7 +2,6 @@ package co.electriccoin.zcash.ui.screen.home
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.NavigationTargets
@ -11,115 +10,61 @@ import co.electriccoin.zcash.ui.common.model.DistributionDimension
import co.electriccoin.zcash.ui.common.model.KeystoneAccount
import co.electriccoin.zcash.ui.common.model.WalletAccount
import co.electriccoin.zcash.ui.common.provider.GetVersionInfoProvider
import co.electriccoin.zcash.ui.common.usecase.GetHomeMessageUseCase
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.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.balance.TransparentBalanceInfo
import co.electriccoin.zcash.ui.screen.home.messages.EnableCurrencyConversionMessageState
import co.electriccoin.zcash.ui.screen.home.messages.HomeMessageState
import co.electriccoin.zcash.ui.screen.home.messages.TransparentBalanceDetectedMessageState
import co.electriccoin.zcash.ui.screen.home.messages.WalletBackupMessageState
import co.electriccoin.zcash.ui.screen.home.messages.WalletDisconnectedMessageState
import co.electriccoin.zcash.ui.screen.home.messages.WalletErrorMessageState
import co.electriccoin.zcash.ui.screen.home.messages.WalletRestoringMessageState
import co.electriccoin.zcash.ui.screen.home.messages.WalletSyncingMessageState
import co.electriccoin.zcash.ui.screen.home.messages.WalletUpdatingMessageState
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.WalletBackupMessageState
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.syncing.WalletSyncingInfo
import co.electriccoin.zcash.ui.screen.home.transparentbalance.TransparentBalanceInfo
import co.electriccoin.zcash.ui.screen.home.updating.WalletUpdatingInfo
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.seed.backup.SeedBackup
import co.electriccoin.zcash.ui.screen.home.backup.WalletBackupDetail
import co.electriccoin.zcash.ui.screen.send.Send
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.WhileSubscribed
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlin.time.Duration.Companion.seconds
class HomeViewModel(
getHomeMessage: GetHomeMessageUseCase,
getVersionInfoProvider: GetVersionInfoProvider,
getSelectedWalletAccountUseCase: GetSelectedWalletAccountUseCase,
private val navigationRouter: NavigationRouter,
private val isRestoreSuccessDialogVisible: IsRestoreSuccessDialogVisibleUseCase,
private val navigateToCoinbase: NavigateToCoinbaseUseCase
private val navigateToCoinbase: NavigateToCoinbaseUseCase,
) : ViewModel() {
@Suppress("MagicNumber")
private val messageState =
flow {
val states =
listOf(
WalletErrorMessageState(
onClick = {}
),
WalletDisconnectedMessageState(onClick = {
navigationRouter.forward(WalletDisconnectedInfo)
}),
WalletRestoringMessageState(progress = 0, onClick = {
navigationRouter.forward(WalletRestoringInfo)
}),
WalletRestoringMessageState(progress = 100, onClick = {
navigationRouter.forward(WalletRestoringInfo)
}),
WalletSyncingMessageState(progress = 0, onClick = {
navigationRouter.forward(WalletSyncingInfo)
}),
WalletSyncingMessageState(progress = 100, onClick = {
navigationRouter.forward(WalletSyncingInfo)
}),
WalletUpdatingMessageState(onClick = {
navigationRouter.forward(WalletUpdatingInfo)
}),
WalletBackupMessageState(
onClick = {
navigationRouter.forward(SeedBackupInfo)
},
onButtonClick = {
navigationRouter.forward(SeedBackup(false))
}
),
TransparentBalanceDetectedMessageState(
subtitle = stringRes(zatoshi = Zatoshi(1000)),
onClick = {
navigationRouter.forward(TransparentBalanceInfo)
},
onButtonClick = {
// navigationRouter.forward(TransparentBalanceInfo)
},
),
EnableCurrencyConversionMessageState(
onClick = {
navigationRouter.forward(ExchangeRateOptIn)
},
onButtonClick = {
navigationRouter.forward(ExchangeRateOptIn)
},
)
)
var index = 0
while (true) {
emit(states[index])
delay(3.seconds)
if (index == states.lastIndex) {
emit(null)
delay(10.seconds)
index = 0
} else {
index += 1
}
}
}
private val messageState = getHomeMessage
.observe()
.map { createMessageState(it) }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(),
initialValue = null
)
private val isRestoreDialogVisible: Flow<Boolean?> =
isRestoreSuccessDialogVisible
@ -130,10 +75,10 @@ class HomeViewModel(
initialValue = null
)
val restoreDialogState: StateFlow<HomeRestoreDialogState?> =
val restoreDialogState: StateFlow<HomeRestoreSuccessDialogState?> =
isRestoreDialogVisible
.map { isVisible ->
HomeRestoreDialogState(
HomeRestoreSuccessDialogState(
onClick = ::onRestoreDialogSeenClick
).takeIf { isVisible == true }
}.stateIn(
@ -203,6 +148,48 @@ class HomeViewModel(
message = messageState
)
private fun createMessageState(it: HomeMessageData?) = when (it) {
is HomeMessageData.Backup -> WalletBackupMessageState(
onClick = ::onWalletBackupMessageClick,
onButtonClick = ::onWalletBackupMessageButtonClick,
)
HomeMessageData.Disconnected -> WalletDisconnectedMessageState(
onClick = ::onWalletDisconnectedMessageClick
)
HomeMessageData.EnableCurrencyConversion -> EnableCurrencyConversionMessageState(
onClick = ::onEnableCurrencyConversionClick,
onButtonClick = ::onEnableCurrencyConversionClick
)
is HomeMessageData.Error -> WalletErrorMessageState(
onClick = { onWalletErrorMessageClick(it) }
)
is HomeMessageData.Restoring -> WalletRestoringMessageState(
progress = it.progress,
onClick = ::onWalletRestoringMessageClick
)
is HomeMessageData.Syncing -> WalletSyncingMessageState(
progress = it.progress,
onClick = ::onWalletSyncingMessageClick
)
is HomeMessageData.TransparentBalance -> TransparentBalanceMessageState(
subtitle = stringRes(zatoshi = it.zatoshi),
onClick = ::onTransparentBalanceMessageClick,
onButtonClick = ::onTransparentBalanceMessageButtonClick,
)
HomeMessageData.Updating -> WalletUpdatingMessageState(
onClick = ::onWalletUpdatingMessageClick
)
null -> null
}
private fun onRestoreDialogSeenClick() =
viewModelScope.launch {
isRestoreSuccessDialogVisible.setSeen()
@ -232,4 +219,61 @@ class HomeViewModel(
private fun onRequestClick() {
navigationRouter.forward("${NavigationTargets.REQUEST}/${ReceiveAddressType.Unified.ordinal}")
}
private fun onWalletUpdatingMessageClick() {
navigationRouter.forward(WalletUpdatingInfo)
}
private fun onWalletSyncingMessageClick() {
navigationRouter.forward(WalletSyncingInfo)
}
private fun onWalletRestoringMessageClick() {
navigationRouter.forward(WalletRestoringInfo)
}
private fun onEnableCurrencyConversionClick() {
navigationRouter.forward(ExchangeRateOptIn)
}
private fun onWalletDisconnectedMessageClick() {
navigationRouter.forward(WalletDisconnectedInfo)
}
private fun onWalletBackupMessageClick() {
navigationRouter.forward(SeedBackupInfo)
}
private fun onWalletBackupMessageButtonClick() {
navigationRouter.forward(WalletBackupDetail(false))
}
private fun onTransparentBalanceMessageClick() {
navigationRouter.forward(TransparentBalanceInfo)
}
private fun onTransparentBalanceMessageButtonClick(): Nothing {
TODO()
}
private fun onWalletErrorMessageClick(homeMessageData: HomeMessageData.Error): Nothing {
// statusText =
// context.getString(
// R.string.balances_status_error_simple,
// context.getString(R.string.app_name)
// )
// statusAction =
// StatusAction.Error(
// details =
// context.getString(
// R.string.balances_status_error_dialog_cause,
// walletSnapshot.synchronizerError.getCauseMessage()
// ?: context.getString(R.string.balances_status_error_dialog_cause_unknown),
// walletSnapshot.synchronizerError.getStackTrace(limit = STACKTRACE_LIMIT)
// ?: context.getString(R.string.balances_status_error_dialog_stacktrace_unknown)
// ),
// fullStackTrace = walletSnapshot.synchronizerError.getStackTrace(limit = null)
// )
TODO()
}
}

View File

@ -0,0 +1,22 @@
package co.electriccoin.zcash.ui.screen.home.backup
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.serialization.Serializable
import org.koin.androidx.compose.koinViewModel
import org.koin.core.parameter.parametersOf
@Composable
fun AndroidWalletBackupDetail(args: WalletBackupDetail) {
val viewModel = koinViewModel<WalletBackupDetailViewModel> { parametersOf(args) }
val state by viewModel.state.collectAsStateWithLifecycle()
BackHandler { state.onBack() }
WalletBackupDetailView(state = state)
}
@Serializable
data class WalletBackupDetail(
val isOpenedFromSeedBackupInfo: Boolean
)

View File

@ -0,0 +1,19 @@
package co.electriccoin.zcash.ui.screen.home.backup
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.serialization.Serializable
import org.koin.androidx.compose.koinViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AndroidWalletBackupInfo() {
val viewModel = koinViewModel<WalletBackupInfoViewModel>()
val state by viewModel.state.collectAsStateWithLifecycle()
WalletBackupInfoView(state = state)
}
@Serializable
object SeedBackupInfo

View File

@ -0,0 +1,10 @@
package co.electriccoin.zcash.ui.screen.home.backup
import androidx.compose.runtime.Immutable
@Immutable
data class WalletBackupDetailState(
val onBack: () -> Unit,
val onNextClick: () -> Unit,
val onInfoClick: () -> Unit,
)

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.seed.backup
package co.electriccoin.zcash.ui.screen.home.backup
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
@ -20,7 +20,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.design.component.ButtonState
import co.electriccoin.zcash.ui.design.component.HorizontalSpacer
import co.electriccoin.zcash.ui.design.component.IconButtonState
@ -38,12 +37,11 @@ import co.electriccoin.zcash.ui.design.util.scaffoldPadding
import co.electriccoin.zcash.ui.design.util.stringRes
@Composable
fun SeedBackupView(
state: SeedBackupState,
appBarState: TopAppBarSubTitleState,
fun WalletBackupDetailView(
state: WalletBackupDetailState,
) {
Scaffold(
topBar = { AppBar(state = state, subTitleState = appBarState) }
topBar = { AppBar(state = state) }
) { paddingValues ->
Content(
modifier = Modifier.scaffoldPadding(paddingValues),
@ -54,18 +52,11 @@ fun SeedBackupView(
@Composable
private fun AppBar(
state: SeedBackupState,
subTitleState: TopAppBarSubTitleState,
state: WalletBackupDetailState,
modifier: Modifier = Modifier,
) {
ZashiSmallTopAppBar(
title = stringResource(R.string.wallet_backup_title),
subtitle =
when (subTitleState) {
TopAppBarSubTitleState.Disconnected -> stringResource(id = R.string.disconnected_label)
TopAppBarSubTitleState.Restoring -> stringResource(id = R.string.restoring_wallet_label)
TopAppBarSubTitleState.None -> null
},
modifier = modifier,
navigationAction = {
ZashiTopAppBarBackNavigation(onBack = state.onBack)
@ -85,7 +76,7 @@ private fun AppBar(
@Composable
private fun Content(
state: SeedBackupState,
state: WalletBackupDetailState,
modifier: Modifier = Modifier,
) {
Column(
@ -202,10 +193,9 @@ private fun Item(
@PreviewScreens
private fun Preview() =
ZcashTheme {
SeedBackupView(
appBarState = TopAppBarSubTitleState.None,
WalletBackupDetailView(
state =
SeedBackupState(
WalletBackupDetailState(
onBack = {},
onNextClick = {},
onInfoClick = {}

View File

@ -0,0 +1,43 @@
package co.electriccoin.zcash.ui.screen.home.backup
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.common.usecase.NavigateToWalletBackupUseCase
import co.electriccoin.zcash.ui.screen.restore.info.SeedInfo
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
class WalletBackupDetailViewModel(
private val args: WalletBackupDetail,
private val navigationRouter: NavigationRouter,
private val navigateToWalletBackup: NavigateToWalletBackupUseCase
) : ViewModel() {
val state = MutableStateFlow(
WalletBackupDetailState(
onBack = ::onBack,
onNextClick = ::onNextClick,
onInfoClick = ::onInfoClick
)
).asStateFlow()
private fun onNextClick() =
viewModelScope.launch {
navigateToWalletBackup(true)
}
private fun onInfoClick() {
navigationRouter.forward(SeedInfo)
}
private fun onBack() {
if (args.isOpenedFromSeedBackupInfo) {
navigationRouter.replace(SeedBackupInfo)
} else {
navigationRouter.back()
}
}
}

View File

@ -0,0 +1,10 @@
package co.electriccoin.zcash.ui.screen.home.backup
import co.electriccoin.zcash.ui.design.component.ButtonState
import co.electriccoin.zcash.ui.design.component.ModalBottomSheetState
data class WalletBackupInfoState(
override val onBack: () -> Unit,
val primaryButton: ButtonState,
val secondaryButton: ButtonState
) : ModalBottomSheetState

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.home
package co.electriccoin.zcash.ui.screen.home.backup
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
@ -13,7 +13,6 @@ import androidx.compose.ui.res.painterResource
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 co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.ButtonState
import co.electriccoin.zcash.ui.design.component.Spacer
@ -27,30 +26,16 @@ 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.stringRes
import co.electriccoin.zcash.ui.screen.seed.backup.SeedBackup
import kotlinx.serialization.Serializable
import org.koin.compose.koinInject
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AndroidSeedBackupInfo() {
val navigationRouter = koinInject<NavigationRouter>()
Content(
onBack = { navigationRouter.back() },
onPositiveClick = { navigationRouter.replace(SeedBackup(true)) }
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun Content(
onBack: () -> Unit,
onPositiveClick: () -> Unit,
fun WalletBackupInfoView(
state: WalletBackupInfoState?,
sheetState: SheetState = rememberScreenModalBottomSheetState(),
) {
ZashiScreenModalBottomSheet(
sheetState = sheetState,
onDismissRequest = onBack
state = state
) {
Column(
modifier =
@ -101,21 +86,13 @@ private fun Content(
Spacer(32.dp)
ZashiButton(
modifier = Modifier.fillMaxWidth(),
state =
ButtonState(
text = stringRes(R.string.general_remind_me_later),
onClick = onBack
),
state = it.secondaryButton,
colors = ZashiButtonDefaults.secondaryColors()
)
Spacer(4.dp)
ZashiButton(
modifier = Modifier.fillMaxWidth(),
state =
ButtonState(
text = stringRes(R.string.general_ok),
onClick = onPositiveClick
)
state = it.primaryButton
)
}
}
@ -126,11 +103,17 @@ private fun Content(
@Composable
private fun Preview() =
ZcashTheme {
Content(
onBack = {},
onPositiveClick = {}
WalletBackupInfoView(
WalletBackupInfoState(
onBack = {},
secondaryButton = ButtonState(
text = stringRes(R.string.general_remind_me_later),
onClick = {}
),
primaryButton = ButtonState(
text = stringRes(R.string.general_ok),
onClick = {}
)
)
)
}
@Serializable
object SeedBackupInfo

View File

@ -0,0 +1,39 @@
package co.electriccoin.zcash.ui.screen.home.backup
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.usecase.RemindWalletBackupLaterUseCase
import co.electriccoin.zcash.ui.design.component.ButtonState
import co.electriccoin.zcash.ui.design.util.stringRes
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
class WalletBackupInfoViewModel(
private val navigationRouter: NavigationRouter,
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 fun onPrimaryClick() {
navigationRouter.replace(WalletBackupDetail(true))
}
private fun onRemindMeLaterClick() = viewModelScope.launch { remindWalletBackupLater() }
private fun onBack() = navigationRouter.back()
}

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.home.messages
package co.electriccoin.zcash.ui.screen.home.backup
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.PaddingValues
@ -7,7 +7,6 @@ import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
@ -20,6 +19,8 @@ import co.electriccoin.zcash.ui.design.component.ZashiButton
import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.design.util.stringRes
import co.electriccoin.zcash.ui.screen.home.HomeMessageState
import co.electriccoin.zcash.ui.screen.home.HomeMessageWrapper
@Suppress("ModifierNaming")
@Composable
@ -34,7 +35,6 @@ fun WalletBackupMessage(
onClick = state.onClick,
start = {
Image(
modifier = Modifier.align(Alignment.Top),
painter = painterResource(R.drawable.ic_warning_triangle),
contentDescription = null,
colorFilter = ColorFilter.tint(LocalContentColor.current)

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.home.messages
package co.electriccoin.zcash.ui.screen.home.currency
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.PaddingValues
@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
@ -19,6 +18,8 @@ import co.electriccoin.zcash.ui.design.component.ZashiButton
import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.design.util.stringRes
import co.electriccoin.zcash.ui.screen.home.HomeMessageState
import co.electriccoin.zcash.ui.screen.home.HomeMessageWrapper
@Suppress("ModifierNaming")
@Composable
@ -33,7 +34,6 @@ fun EnableCurrencyConversionMessage(
onClick = state.onClick,
start = {
Image(
modifier = Modifier.align(Alignment.Top),
painter = painterResource(R.drawable.ic_message_currency_conversion),
contentDescription = null,
colorFilter = ColorFilter.tint(LocalContentColor.current)

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.home
package co.electriccoin.zcash.ui.screen.home.disconnected
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column

View File

@ -1,11 +1,10 @@
package co.electriccoin.zcash.ui.screen.home.messages
package co.electriccoin.zcash.ui.screen.home.disconnected
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
@ -15,6 +14,8 @@ import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.BlankSurface
import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.screen.home.HomeMessageState
import co.electriccoin.zcash.ui.screen.home.HomeMessageWrapper
@Suppress("ModifierNaming")
@Composable
@ -29,7 +30,6 @@ fun WalletDisconnectedMessage(
onClick = state.onClick,
start = {
Image(
modifier = Modifier.align(Alignment.Top),
painter = painterResource(R.drawable.ic_message_disconnected),
contentDescription = null,
colorFilter = ColorFilter.tint(LocalContentColor.current)

View File

@ -1,11 +1,10 @@
package co.electriccoin.zcash.ui.screen.home.messages
package co.electriccoin.zcash.ui.screen.home.error
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
@ -15,6 +14,8 @@ import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.BlankSurface
import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.screen.home.HomeMessageState
import co.electriccoin.zcash.ui.screen.home.HomeMessageWrapper
@Suppress("ModifierNaming")
@Composable
@ -29,7 +30,6 @@ fun WalletErrorMessage(
onClick = state.onClick,
start = {
Image(
modifier = Modifier.align(Alignment.Top),
painter = painterResource(R.drawable.ic_warning_triangle),
contentDescription = null,
colorFilter = ColorFilter.tint(LocalContentColor.current)

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.home
package co.electriccoin.zcash.ui.screen.home.restoring
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth

View File

@ -1,7 +1,6 @@
package co.electriccoin.zcash.ui.screen.home.messages
package co.electriccoin.zcash.ui.screen.home.restoring
import androidx.annotation.IntRange
import androidx.compose.animation.core.animateIntAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.size
@ -13,9 +12,11 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.BlankSurface
import co.electriccoin.zcash.ui.design.component.ZashiCircularProgressIndicator
import co.electriccoin.zcash.ui.design.component.ZashiCircularProgressIndicatorByPercent
import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.screen.home.HomeMessageState
import co.electriccoin.zcash.ui.screen.home.HomeMessageWrapper
@Suppress("ModifierNaming")
@Composable
@ -29,7 +30,7 @@ fun WalletRestoringMessage(
contentPadding = contentPadding,
onClick = state.onClick,
start = {
ZashiCircularProgressIndicator(
ZashiCircularProgressIndicatorByPercent(
modifier = Modifier.size(20.dp),
progressPercent = state.progress,
)
@ -49,7 +50,7 @@ fun WalletRestoringMessage(
}
class WalletRestoringMessageState(
@IntRange(from = 0, to = 100) val progress: Int,
val progress: Float,
val onClick: () -> Unit
) : HomeMessageState
@ -57,7 +58,7 @@ class WalletRestoringMessageState(
@Composable
private fun Preview() =
ZcashTheme {
val progress by animateIntAsState(50, label = "progress", animationSpec = tween(10000))
val progress by animateFloatAsState(50f, label = "progress", animationSpec = tween(10000))
BlankSurface {
WalletRestoringMessage(

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.home
package co.electriccoin.zcash.ui.screen.home.syncing
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth

View File

@ -1,7 +1,6 @@
package co.electriccoin.zcash.ui.screen.home.messages
package co.electriccoin.zcash.ui.screen.home.syncing
import androidx.annotation.IntRange
import androidx.compose.animation.core.animateIntAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.size
@ -13,9 +12,11 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.BlankSurface
import co.electriccoin.zcash.ui.design.component.ZashiCircularProgressIndicator
import co.electriccoin.zcash.ui.design.component.ZashiCircularProgressIndicatorByPercent
import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.screen.home.HomeMessageState
import co.electriccoin.zcash.ui.screen.home.HomeMessageWrapper
@Suppress("ModifierNaming")
@Composable
@ -29,7 +30,7 @@ fun WalletSyncingMessage(
contentPadding = contentPadding,
onClick = state.onClick,
start = {
ZashiCircularProgressIndicator(
ZashiCircularProgressIndicatorByPercent(
modifier = Modifier.size(20.dp),
progressPercent = state.progress,
)
@ -49,7 +50,7 @@ fun WalletSyncingMessage(
}
class WalletSyncingMessageState(
@IntRange(from = 0, to = 100) val progress: Int,
val progress: Float,
val onClick: () -> Unit
) : HomeMessageState
@ -57,7 +58,7 @@ class WalletSyncingMessageState(
@Composable
private fun Preview() =
ZcashTheme {
val progress by animateIntAsState(50, label = "progress", animationSpec = tween(10000))
val progress by animateFloatAsState(50f, label = "progress", animationSpec = tween(10000))
BlankSurface {
WalletSyncingMessage(

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.home.balance
package co.electriccoin.zcash.ui.screen.home.transparentbalance
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.home.balance
package co.electriccoin.zcash.ui.screen.home.transparentbalance
import cash.z.ecc.android.sdk.model.Zatoshi
import co.electriccoin.zcash.ui.design.component.ButtonState

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.home.balance
package co.electriccoin.zcash.ui.screen.home.transparentbalance
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column

Some files were not shown because too many files have changed in this diff Show More