[#634] Stop synchronizer when leaving app

* [#634] Collect flows with lifecycle awareness

* [#686] Adopt SDK 1.11.0
This commit is contained in:
Carter Jernigan 2022-12-22 03:38:02 -05:00 committed by GitHub
parent ad89309ec1
commit 4f7c10f4b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 110 additions and 64 deletions

1
.gitignore vendored
View File

@ -20,3 +20,4 @@ local.properties
/.idea/artifacts
/.idea/assetWizardSettings.xml
/.idea/inspectionProfiles/Project_Default.xml
/.idea/androidTestResultsUserPreferences.xml

View File

@ -6,6 +6,8 @@
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
</value>
</option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />

View File

@ -120,7 +120,7 @@ ANDROIDX_COMPOSE_VERSION=1.3.1
ANDROIDX_CONSTRAINTLAYOUT_VERSION=1.0.1
ANDROIDX_CORE_VERSION=1.9.0
ANDROIDX_ESPRESSO_VERSION=3.5.0
ANDROIDX_LIFECYCLE_VERSION=2.5.1
ANDROIDX_LIFECYCLE_VERSION=2.6.0-alpha03
ANDROIDX_NAVIGATION_COMPOSE_VERSION=2.5.3
ANDROIDX_PROFILE_INSTALLER_VERSION=1.3.0-alpha02
ANDROIDX_SECURITY_CRYPTO_VERSION=1.1.0-alpha04
@ -145,7 +145,7 @@ ZCASH_ANDROID_WALLET_PLUGINS_VERSION=1.0.0
ZCASH_BIP39_VERSION=1.0.4
# TODO [#279]: Revert to stable SDK before app release
# TODO [#279]: https://github.com/zcash/secant-android-wallet/issues/279
ZCASH_SDK_VERSION=1.10.0-beta01-SNAPSHOT
ZCASH_SDK_VERSION=1.11.0-beta01-SNAPSHOT
ZXING_VERSION=3.5.0

View File

@ -173,6 +173,7 @@ dependencyResolutionManagement {
library("androidx-core", "androidx.core:core-ktx:$androidxCoreVersion")
library("androidx-constraintlayout", "androidx.constraintlayout:constraintlayout-compose:$androidxConstraintLayoutVersion")
library("androidx-lifecycle-livedata", "androidx.lifecycle:lifecycle-livedata-ktx:$androidxLifecycleVersion")
library("androidx-lifecycle-compose", "androidx.lifecycle:lifecycle-runtime-compose:$androidxLifecycleVersion")
library("androidx-navigation-compose", "androidx.navigation:navigation-compose:$androidxNavigationComposeVersion")
library("androidx-profileinstaller", "androidx.profileinstaller:profileinstaller:$androidxProfileInstallerVersion")
library("androidx-security-crypto", "androidx.security:security-crypto-ktx:$androidxSecurityCryptoVersion")
@ -236,6 +237,7 @@ dependencyResolutionManagement {
"androidx-activity-compose",
"androidx-compose-material-icons-core",
"androidx-compose-material-icons-extended",
"androidx-lifecycle-compose",
"androidx-navigation-compose",
"androidx-viewmodel-compose"
)

View File

@ -17,6 +17,7 @@ import co.electriccoin.zcash.ui.preference.StandardPreferenceSingleton
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.awaitClose
@ -86,21 +87,20 @@ class WalletCoordinator(context: Context) {
flowOf(InternalSynchronizerStatus.NoWallet)
} else {
callbackFlow<InternalSynchronizerStatus.Available> {
val synchronizer = synchronizerMutex.withLock {
val synchronizer = Synchronizer.new(
val closeableSynchronizer = synchronizerMutex.withLock {
Synchronizer.new(
context = context,
zcashNetwork = persistableWallet.network,
lightWalletEndpoint = LightWalletEndpoint.defaultForNetwork(persistableWallet.network),
birthday = persistableWallet.birthday,
seed = persistableWallet.seedPhrase.toByteArray()
)
synchronizer.start(walletScope)
}
trySend(InternalSynchronizerStatus.Available(synchronizer))
trySend(InternalSynchronizerStatus.Available(closeableSynchronizer))
awaitClose {
Twig.info { "Closing flow and stopping synchronizer" }
synchronizer.stop()
Twig.info { "Closing flow and synchronizer" }
closeableSynchronizer.close()
}
}
}
@ -112,11 +112,9 @@ class WalletCoordinator(context: Context) {
* Note that this synchronizer is closed as soon as it stops being collected. For UI use
* cases, see [WalletViewModel].
*/
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
@OptIn(ExperimentalCoroutinesApi::class)
val synchronizer: StateFlow<Synchronizer?> = synchronizerOrLockoutId
.flatMapLatest {
it
}
.flatMapLatest { it }
.map {
when (it) {
is InternalSynchronizerStatus.Available -> it.synchronizer

View File

@ -9,13 +9,13 @@ import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavHostController
import cash.z.ecc.android.sdk.ext.collectWith
import co.electriccoin.zcash.ui.common.BindCompLocalProvider
import co.electriccoin.zcash.ui.design.compat.FontCompat
import co.electriccoin.zcash.ui.design.component.ConfigurationOverride
@ -78,6 +78,7 @@ class MainActivity : ComponentActivity() {
}
}
@OptIn(ExperimentalLifecycleComposeApi::class)
private fun setupUiContent() {
setContent {
Override(configurationOverrideFlow) {
@ -88,7 +89,7 @@ class MainActivity : ComponentActivity() {
.fillMaxHeight()
) {
BindCompLocalProvider {
val isEnoughSpace by storageCheckViewModel.isEnoughSpace.collectAsState()
val isEnoughSpace by storageCheckViewModel.isEnoughSpace.collectAsStateWithLifecycle()
if (isEnoughSpace == false) {
WrapNotEnoughSpace()
} else {
@ -98,18 +99,17 @@ class MainActivity : ComponentActivity() {
}
}
}
}
// Force collection to improve performance; sync can start happening while
// the user is going through the backup flow. Don't use eager collection in the view model,
// so that the collection is still tied to UI lifecycle.
walletViewModel.synchronizer.collectWith(lifecycleScope) {
// Force collection to improve performance; sync can start happening while
// the user is going through the backup flow.
walletViewModel.synchronizer.collectAsStateWithLifecycle()
}
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
private fun MainContent() {
when (val secretState = walletViewModel.secretState.collectAsState().value) {
when (val secretState = walletViewModel.secretState.collectAsStateWithLifecycle().value) {
SecretState.Loading -> {
// For now, keep displaying splash screen using condition above.
// In the future, we might consider displaying something different here.

View File

@ -5,7 +5,8 @@ package co.electriccoin.zcash.ui.screen.address
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.screen.address.view.WalletAddresses
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
@ -17,6 +18,7 @@ internal fun MainActivity.WrapWalletAddresses(
WrapWalletAddresses(this, goBack)
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
private fun WrapWalletAddresses(
activity: ComponentActivity,
@ -24,7 +26,8 @@ private fun WrapWalletAddresses(
) {
val walletViewModel by activity.viewModels<WalletViewModel>()
val walletAddresses = walletViewModel.addresses.collectAsState().value
val walletAddresses = walletViewModel.addresses.collectAsStateWithLifecycle().value
if (null == walletAddresses) {
// Display loading indicator
} else {

View File

@ -27,7 +27,6 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
@ -38,6 +37,8 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cash.z.ecc.sdk.fixture.PersistableWalletFixture
import cash.z.ecc.sdk.model.PersistableWallet
import co.electriccoin.zcash.spackle.model.Index
@ -78,7 +79,7 @@ fun ComposablePreview() {
/**
* @param onComplete Callback when the user has completed the backup test.
*/
@OptIn(ExperimentalMaterial3Api::class)
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLifecycleComposeApi::class)
@Composable
@Suppress("LongParameterList")
fun BackupWallet(
@ -89,7 +90,7 @@ fun BackupWallet(
onComplete: () -> Unit,
onChoicesChanged: ((choicesCount: Int) -> Unit)?
) {
val currentBackupStage = backupState.current.collectAsState().value
val currentBackupStage = backupState.current.collectAsStateWithLifecycle().value
Scaffold(
topBar = {
@ -123,6 +124,7 @@ fun BackupWallet(
}
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
fun BackupMainContent(
paddingValues: PaddingValues,
@ -138,7 +140,7 @@ fun BackupMainContent(
bottom = paddingValues.calculateBottomPadding()
)
) {
when (backupState.current.collectAsState().value) {
when (backupState.current.collectAsStateWithLifecycle().value) {
is BackupStage.EducationOverview -> EducationOverview()
is BackupStage.EducationRecoveryPhrase -> EducationRecoveryPhrase()
is BackupStage.Seed -> SeedPhrase(wallet)
@ -216,6 +218,7 @@ private data class TestChoice(val originalIndex: Index, val word: String)
* - It is possible for the same word to appear twice in the word choices
* - The test answer ordering is not randomized, to ensure it can never be in the correct order to start with
*/
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
private fun TestInProgress(
splitSeedPhrase: List<String>,
@ -233,7 +236,7 @@ private fun TestInProgress(
@Suppress("MagicNumber")
listOf(it[1], it[0], it[3], it[2])
}
val currentSelectedTestChoice = selectedTestChoices.current.collectAsState().value
val currentSelectedTestChoice = selectedTestChoices.current.collectAsStateWithLifecycle().value
if (currentSelectedTestChoice.size == testIndices.size) {
if (currentSelectedTestChoice.all { splitSeedPhrase[it.key.value] == it.value }) {
// the user got the test correct

View File

@ -5,8 +5,9 @@ package co.electriccoin.zcash.ui.screen.home
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.spackle.EmulatorWtfUtil
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
import co.electriccoin.zcash.ui.BuildConfig
@ -34,6 +35,7 @@ internal fun MainActivity.WrapHome(
)
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
internal fun WrapHome(
activity: ComponentActivity,
@ -49,13 +51,14 @@ internal fun WrapHome(
AppUpdateCheckerImp.new()
)
}
val updateAvailable = checkUpdateViewModel.updateInfo.collectAsState().value.let {
val updateAvailable = checkUpdateViewModel.updateInfo.collectAsStateWithLifecycle().value.let {
it?.appUpdateInfo != null && it.state == UpdateState.Prepared
}
val walletViewModel by activity.viewModels<WalletViewModel>()
val walletSnapshot = walletViewModel.walletSnapshot.collectAsState().value
val walletSnapshot = walletViewModel.walletSnapshot.collectAsStateWithLifecycle().value
if (null == walletSnapshot) {
// Display loading indicator
} else {
@ -67,9 +70,11 @@ internal fun WrapHome(
!FirebaseTestLabUtil.isFirebaseTestLab(context) &&
!EmulatorWtfUtil.isEmulatorWtf(context)
val transactionSnapshot = walletViewModel.transactionSnapshot.collectAsStateWithLifecycle().value
Home(
walletSnapshot,
walletViewModel.transactionSnapshot.collectAsState().value,
transactionSnapshot,
goScan = goScan,
goRequest = goRequest,
goSend = goSend,

View File

@ -7,8 +7,9 @@ import android.content.Context
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.sdk.fixture.SeedPhraseFixture
import cash.z.ecc.sdk.model.PersistableWallet
@ -30,6 +31,7 @@ internal fun MainActivity.WrapOnboarding() {
WrapOnboarding(this)
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
internal fun WrapOnboarding(
activity: ComponentActivity
@ -46,7 +48,7 @@ internal fun WrapOnboarding(
!EmulatorWtfUtil.isEmulatorWtf(applicationContext)
// TODO [#383]: https://github.com/zcash/secant-android-wallet/issues/383
if (!onboardingViewModel.isImporting.collectAsState().value) {
if (!onboardingViewModel.isImporting.collectAsStateWithLifecycle().value) {
Onboarding(
onboardingState = onboardingViewModel.onboardingState,
isDebugMenuEnabled = isDebugMenuEnabled,
@ -93,6 +95,7 @@ internal fun WrapOnboarding(
}
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
private fun WrapRestore(activity: ComponentActivity) {
val walletViewModel by activity.viewModels<WalletViewModel>()
@ -101,7 +104,7 @@ private fun WrapRestore(activity: ComponentActivity) {
val applicationContext = LocalContext.current.applicationContext
when (val completeWordList = restoreViewModel.completeWordList.collectAsState().value) {
when (val completeWordList = restoreViewModel.completeWordList.collectAsStateWithLifecycle().value) {
CompleteWordSetState.Loading -> {
// Although it might perform IO, it should be relatively fast.
// Consider whether to display indeterminate progress here.

View File

@ -24,7 +24,6 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
@ -36,6 +35,8 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.Body
import co.electriccoin.zcash.ui.design.component.GradientSurface
@ -69,7 +70,7 @@ fun ComposablePreview() {
* @param onImportWallet Callback when the user decides to import an existing wallet.
* @param onCreateWallet Callback when the user decides to create a new wallet.
*/
@OptIn(ExperimentalMaterial3Api::class)
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLifecycleComposeApi::class)
@Composable
fun Onboarding(
onboardingState: OnboardingState,
@ -78,7 +79,7 @@ fun Onboarding(
onCreateWallet: () -> Unit,
onFixtureWallet: () -> Unit
) {
val currentStage = onboardingState.current.collectAsState().value
val currentStage = onboardingState.current.collectAsStateWithLifecycle().value
Scaffold(
topBar = {
OnboardingTopAppBar(onboardingState, isDebugMenuEnabled, onFixtureWallet)
@ -95,13 +96,13 @@ fun Onboarding(
}
@Composable
@OptIn(ExperimentalMaterial3Api::class)
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLifecycleComposeApi::class)
private fun OnboardingTopAppBar(
onboardingState: OnboardingState,
isDebugMenuEnabled: Boolean,
onFixtureWallet: () -> Unit
) {
val currentStage = onboardingState.current.collectAsState().value
val currentStage = onboardingState.current.collectAsStateWithLifecycle().value
TopAppBar(
title = { Text(text = stringResource(id = R.string.app_name)) },
@ -149,6 +150,7 @@ private fun DebugMenu(onFixtureWallet: () -> Unit) {
* @param onImportWallet Callback when the user decides to import an existing wallet.
* @param onCreateWallet Callback when the user decides to create a new wallet.
*/
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
fun OnboardingMainContent(
paddingValues: PaddingValues,
@ -157,7 +159,7 @@ fun OnboardingMainContent(
Column(
Modifier.padding(top = paddingValues.calculateTopPadding())
) {
val onboardingStage = onboardingState.current.collectAsState().value
val onboardingStage = onboardingState.current.collectAsStateWithLifecycle().value
when (onboardingStage) {
OnboardingStage.ShieldedByDefault -> ShieldedByDefault(paddingValues)

View File

@ -5,7 +5,8 @@ package co.electriccoin.zcash.ui.screen.profile
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cash.z.ecc.sdk.model.WalletAddresses
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
@ -34,6 +35,7 @@ internal fun MainActivity.WrapProfile(
)
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
@Suppress("LongParameterList")
internal fun WrapProfile(
@ -47,7 +49,7 @@ internal fun WrapProfile(
onAbout: () -> Unit
) {
val viewModel by activity.viewModels<WalletViewModel>()
val walletAddresses = viewModel.addresses.collectAsState().value
val walletAddresses = viewModel.addresses.collectAsStateWithLifecycle().value
WrapProfile(
walletAddresses,

View File

@ -7,7 +7,8 @@ import android.content.Intent
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cash.z.ecc.sdk.model.ZecRequest
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R
@ -22,13 +23,14 @@ internal fun MainActivity.WrapRequest(
WrapRequest(this, goBack)
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
private fun WrapRequest(
activity: ComponentActivity,
goBack: () -> Unit
) {
val walletViewModel by activity.viewModels<WalletViewModel>()
val walletAddresses = walletViewModel.addresses.collectAsState().value
val walletAddresses = walletViewModel.addresses.collectAsStateWithLifecycle().value
if (null == walletAddresses) {
// Display loading indicator

View File

@ -52,6 +52,8 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cash.z.ecc.sdk.model.SeedPhraseValidation
import co.electriccoin.zcash.spackle.model.Index
import co.electriccoin.zcash.ui.R
@ -185,6 +187,7 @@ private fun RestoreTopAppBar(onBack: () -> Unit, onClear: () -> Unit) {
// TODO [#672] Implement custom seed phrase pasting for wallet import
// TODO [#672] https://github.com/zcash/secant-android-wallet/issues/672
@OptIn(ExperimentalLifecycleComposeApi::class)
@Suppress("UNUSED_PARAMETER", "LongParameterList")
@Composable
private fun RestoreMainContent(
@ -195,7 +198,7 @@ private fun RestoreMainContent(
parseResult: ParseResult,
paste: () -> String?
) {
val currentUserWordList = userWordList.current.collectAsState().value
val currentUserWordList = userWordList.current.collectAsStateWithLifecycle().value
val scrollState = rememberScrollState()
val scope = rememberCoroutineScope()

View File

@ -6,9 +6,10 @@ import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R
@ -29,6 +30,7 @@ internal fun MainActivity.WrapScanValidator(
)
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
private fun WrapScanValidator(
activity: ComponentActivity,
@ -37,7 +39,8 @@ private fun WrapScanValidator(
) {
val walletViewModel by activity.viewModels<WalletViewModel>()
val synchronizer = walletViewModel.synchronizer.collectAsState().value
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
if (synchronizer == null) {
// Display loading indicator
} else {

View File

@ -5,7 +5,8 @@ package co.electriccoin.zcash.ui.screen.seed
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.screen.backup.copyToClipboard
import co.electriccoin.zcash.ui.screen.home.viewmodel.SecretState
@ -19,6 +20,7 @@ internal fun MainActivity.WrapSeed(
WrapSeed(this, goBack)
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
private fun WrapSeed(
activity: ComponentActivity,
@ -27,14 +29,15 @@ private fun WrapSeed(
val walletViewModel by activity.viewModels<WalletViewModel>()
val persistableWallet = run {
val secretState = walletViewModel.secretState.collectAsState().value
val secretState = walletViewModel.secretState.collectAsStateWithLifecycle().value
if (secretState is SecretState.Ready) {
secretState.persistableWallet
} else {
null
}
}
val synchronizer = walletViewModel.synchronizer.collectAsState().value
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
if (null == synchronizer || null == persistableWallet) {
// Display loading indicator
} else {

View File

@ -5,8 +5,9 @@ package co.electriccoin.zcash.ui.screen.send
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cash.z.ecc.sdk.send
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.screen.home.model.spendableBalance
@ -21,6 +22,7 @@ internal fun MainActivity.WrapSend(
WrapSend(this, goBack)
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
private fun WrapSend(
activity: ComponentActivity,
@ -29,9 +31,11 @@ private fun WrapSend(
val walletViewModel by activity.viewModels<WalletViewModel>()
val scope = rememberCoroutineScope()
val synchronizer = walletViewModel.synchronizer.collectAsState().value
val spendableBalance = walletViewModel.walletSnapshot.collectAsState().value?.spendableBalance()
val spendingKey = walletViewModel.spendingKey.collectAsState().value
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
val spendableBalance = walletViewModel.walletSnapshot.collectAsStateWithLifecycle().value?.spendableBalance()
val spendingKey = walletViewModel.spendingKey.collectAsStateWithLifecycle().value
if (null == synchronizer || null == spendableBalance || null == spendingKey) {
// Display loading indicator
} else {

View File

@ -5,7 +5,8 @@ package co.electriccoin.zcash.ui.screen.settings
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.screen.onboarding.viewmodel.OnboardingViewModel
@ -23,6 +24,7 @@ internal fun MainActivity.WrapSettings(
)
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
private fun WrapSettings(
activity: ComponentActivity,
@ -31,7 +33,8 @@ private fun WrapSettings(
) {
val walletViewModel by activity.viewModels<WalletViewModel>()
val synchronizer = walletViewModel.synchronizer.collectAsState().value
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
if (null == synchronizer) {
// Display loading indicator
} else {

View File

@ -7,9 +7,10 @@ import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.screen.support.model.SupportInfo
@ -26,13 +27,14 @@ internal fun MainActivity.WrapSupport(
WrapSupport(this, goBack)
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
internal fun WrapSupport(
activity: ComponentActivity,
goBack: () -> Unit
) {
val viewModel by activity.viewModels<SupportViewModel>()
val supportMessage = viewModel.supportInfo.collectAsState().value
val supportMessage = viewModel.supportInfo.collectAsStateWithLifecycle().value
val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()

View File

@ -7,9 +7,10 @@ import androidx.annotation.VisibleForTesting
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.screen.home.viewmodel.CheckUpdateViewModel
@ -26,6 +27,7 @@ internal fun MainActivity.WrapCheckForUpdate() {
WrapCheckForUpdate(this)
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
private fun WrapCheckForUpdate(activity: ComponentActivity) {
// TODO [#382]: https://github.com/zcash/secant-android-wallet/issues/382
@ -38,7 +40,7 @@ private fun WrapCheckForUpdate(activity: ComponentActivity) {
)
}
val updateInfo = checkUpdateViewModel.updateInfo.collectAsState().value
val updateInfo = checkUpdateViewModel.updateInfo.collectAsStateWithLifecycle().value
updateInfo?.let {
if (it.appUpdateInfo != null && it.state == UpdateState.Prepared) {
@ -53,6 +55,7 @@ private fun WrapCheckForUpdate(activity: ComponentActivity) {
}
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
private fun WrapUpdate(
activity: ComponentActivity,
@ -69,7 +72,7 @@ private fun WrapUpdate(
val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
val updateInfo = viewModel.updateInfo.collectAsState().value
val updateInfo = viewModel.updateInfo.collectAsStateWithLifecycle().value
when (updateInfo.state) {
UpdateState.Done, UpdateState.Canceled -> {

View File

@ -5,8 +5,9 @@ package co.electriccoin.zcash.ui.screen.warning
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.screen.warning.view.NotEnoughSpaceView
import co.electriccoin.zcash.ui.screen.warning.viewmodel.StorageCheckViewModel
@ -16,10 +17,11 @@ fun MainActivity.WrapNotEnoughSpace() {
WrapNotEnoughSpace(this)
}
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
private fun WrapNotEnoughSpace(activity: ComponentActivity) {
val storageCheckViewModel by activity.viewModels<StorageCheckViewModel>()
val spaceRequiredToContinue by storageCheckViewModel.spaceRequiredToContinueMegabytes.collectAsState()
val spaceRequiredToContinue by storageCheckViewModel.spaceRequiredToContinueMegabytes.collectAsStateWithLifecycle()
NotEnoughSpaceView(
storageSpaceRequiredGigabytes = storageCheckViewModel.requiredStorageSpaceGigabytes,