Message visibility based on foreground/background
This commit is contained in:
parent
c45d672995
commit
1315ba04ec
|
@ -19,6 +19,7 @@ import co.electriccoin.zcash.spackle.StrictModeCompat
|
|||
import co.electriccoin.zcash.spackle.Twig
|
||||
import co.electriccoin.zcash.ui.common.provider.ApplicationStateProvider
|
||||
import co.electriccoin.zcash.ui.common.repository.FlexaRepository
|
||||
import co.electriccoin.zcash.ui.common.repository.HomeMessageCacheRepository
|
||||
import co.electriccoin.zcash.ui.preference.StandardPreferenceKeys
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.android.ext.android.inject
|
||||
|
@ -31,6 +32,7 @@ class ZcashApplication : CoroutineApplication() {
|
|||
private val flexaRepository by inject<FlexaRepository>()
|
||||
private val applicationStateProvider: ApplicationStateProvider by inject()
|
||||
private val getAvailableCrashReporters: CrashReportersProvider by inject()
|
||||
private val homeMessageCacheRepository: HomeMessageCacheRepository by inject()
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
@ -68,6 +70,7 @@ class ZcashApplication : CoroutineApplication() {
|
|||
configureAnalytics()
|
||||
|
||||
flexaRepository.init()
|
||||
homeMessageCacheRepository.init()
|
||||
}
|
||||
|
||||
private fun configureLogging() {
|
||||
|
|
|
@ -8,6 +8,8 @@ import co.electriccoin.zcash.ui.common.repository.ExchangeRateRepository
|
|||
import co.electriccoin.zcash.ui.common.repository.ExchangeRateRepositoryImpl
|
||||
import co.electriccoin.zcash.ui.common.repository.FlexaRepository
|
||||
import co.electriccoin.zcash.ui.common.repository.FlexaRepositoryImpl
|
||||
import co.electriccoin.zcash.ui.common.repository.HomeMessageCacheRepository
|
||||
import co.electriccoin.zcash.ui.common.repository.HomeMessageCacheRepositoryImpl
|
||||
import co.electriccoin.zcash.ui.common.repository.KeystoneProposalRepository
|
||||
import co.electriccoin.zcash.ui.common.repository.KeystoneProposalRepositoryImpl
|
||||
import co.electriccoin.zcash.ui.common.repository.ShieldFundsRepository
|
||||
|
@ -36,4 +38,5 @@ val repositoryModule =
|
|||
singleOf(::TransactionFilterRepositoryImpl) bind TransactionFilterRepository::class
|
||||
singleOf(::ZashiProposalRepositoryImpl) bind ZashiProposalRepository::class
|
||||
singleOf(::ShieldFundsRepositoryImpl) bind ShieldFundsRepository::class
|
||||
singleOf(::HomeMessageCacheRepositoryImpl) bind HomeMessageCacheRepository::class
|
||||
}
|
||||
|
|
|
@ -185,7 +185,7 @@ val useCaseModule =
|
|||
factoryOf(::GetKeystoneStatusUseCase)
|
||||
factoryOf(::GetCoinbaseStatusUseCase)
|
||||
factoryOf(::GetFlexaStatusUseCase)
|
||||
factoryOf(::GetHomeMessageUseCase)
|
||||
singleOf(::GetHomeMessageUseCase)
|
||||
factoryOf(::OnUserSavedWalletBackupUseCase)
|
||||
factoryOf(::RemindWalletBackupLaterUseCase)
|
||||
factoryOf(::RemindShieldFundsLaterUseCase)
|
||||
|
|
|
@ -34,6 +34,7 @@ import co.electriccoin.zcash.ui.NavigationTargets.SETTINGS
|
|||
import co.electriccoin.zcash.ui.NavigationTargets.SUPPORT
|
||||
import co.electriccoin.zcash.ui.NavigationTargets.WHATS_NEW
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalNavController
|
||||
import co.electriccoin.zcash.ui.common.datasource.MessageAvailabilityDataSource
|
||||
import co.electriccoin.zcash.ui.common.provider.ApplicationStateProvider
|
||||
import co.electriccoin.zcash.ui.common.provider.isInForeground
|
||||
import co.electriccoin.zcash.ui.design.LocalKeyboardManager
|
||||
|
@ -141,6 +142,7 @@ internal fun MainActivity.Navigation() {
|
|||
val flexaViewModel = koinViewModel<FlexaViewModel>()
|
||||
val navigationRouter = koinInject<NavigationRouter>()
|
||||
val sheetStateManager = LocalSheetStateManager.current
|
||||
val messageAvailabilityDataSource = koinInject<MessageAvailabilityDataSource>()
|
||||
|
||||
// Helper properties for triggering the system security UI from callbacks
|
||||
val (exportPrivateDataAuthentication, setExportPrivateDataAuthentication) =
|
||||
|
@ -153,14 +155,16 @@ internal fun MainActivity.Navigation() {
|
|||
navController,
|
||||
flexaViewModel,
|
||||
keyboardManager,
|
||||
sheetStateManager
|
||||
sheetStateManager,
|
||||
messageAvailabilityDataSource
|
||||
) {
|
||||
NavigatorImpl(
|
||||
activity = this@Navigation,
|
||||
navController = navController,
|
||||
flexaViewModel = flexaViewModel,
|
||||
keyboardManager = keyboardManager,
|
||||
sheetStateManager = sheetStateManager
|
||||
sheetStateManager = sheetStateManager,
|
||||
messageAvailabilityDataSource = messageAvailabilityDataSource
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.activity.ComponentActivity
|
|||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.NavOptionsBuilder
|
||||
import androidx.navigation.serialization.generateHashCode
|
||||
import co.electriccoin.zcash.ui.common.datasource.MessageAvailabilityDataSource
|
||||
import co.electriccoin.zcash.ui.design.KeyboardManager
|
||||
import co.electriccoin.zcash.ui.design.SheetStateManager
|
||||
import co.electriccoin.zcash.ui.screen.ExternalUrl
|
||||
|
@ -25,6 +26,7 @@ class NavigatorImpl(
|
|||
private val flexaViewModel: FlexaViewModel,
|
||||
private val keyboardManager: KeyboardManager,
|
||||
private val sheetStateManager: SheetStateManager,
|
||||
private val messageAvailabilityDataSource: MessageAvailabilityDataSource,
|
||||
) : Navigator {
|
||||
override suspend fun executeCommand(command: NavigationCommand) {
|
||||
keyboardManager.close()
|
||||
|
@ -85,6 +87,7 @@ class NavigatorImpl(
|
|||
throw UnsupportedOperationException("External url can be opened as last screen only")
|
||||
}
|
||||
|
||||
messageAvailabilityDataSource.onThirdPartyUiShown()
|
||||
WebBrowserUtil.startActivity(activity, route.url)
|
||||
}
|
||||
|
||||
|
@ -125,6 +128,7 @@ class NavigatorImpl(
|
|||
throw UnsupportedOperationException("External url can be opened as last screen only")
|
||||
}
|
||||
|
||||
messageAvailabilityDataSource.onThirdPartyUiShown()
|
||||
WebBrowserUtil.startActivity(activity, route.url)
|
||||
}
|
||||
|
||||
|
@ -149,6 +153,10 @@ class NavigatorImpl(
|
|||
else -> navController.executeNavigation(route = route)
|
||||
}
|
||||
}
|
||||
|
||||
if (command.routes.lastOrNull() in listOf(ExternalUrl, co.electriccoin.zcash.ui.screen.flexa.Flexa) ) {
|
||||
messageAvailabilityDataSource.onThirdPartyUiShown()
|
||||
}
|
||||
}
|
||||
|
||||
private fun NavHostController.executeNavigation(
|
||||
|
@ -171,11 +179,11 @@ class NavigatorImpl(
|
|||
}
|
||||
|
||||
private fun createFlexaFlow(flexaViewModel: FlexaViewModel) {
|
||||
messageAvailabilityDataSource.onThirdPartyUiShown()
|
||||
Flexa
|
||||
.buildSpend()
|
||||
.onTransactionRequest { result ->
|
||||
flexaViewModel.createTransaction(result)
|
||||
}.build()
|
||||
.onTransactionRequest { result -> flexaViewModel.createTransaction(result) }
|
||||
.build()
|
||||
.open(activity)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,42 +5,75 @@ import co.electriccoin.zcash.ui.common.provider.ApplicationStateProvider
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
interface MessageAvailabilityDataSource {
|
||||
val canShowMessage: Boolean
|
||||
fun observe(): StateFlow<Boolean>
|
||||
fun observe(): Flow<Boolean>
|
||||
fun onMessageShown()
|
||||
fun onThirdPartyUiShown()
|
||||
}
|
||||
|
||||
class MessageAvailabilityDataSourceImpl(
|
||||
private val applicationStateProvider: ApplicationStateProvider
|
||||
): MessageAvailabilityDataSource {
|
||||
applicationStateProvider: ApplicationStateProvider
|
||||
) : MessageAvailabilityDataSource {
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
|
||||
|
||||
private val state = MutableStateFlow(true)
|
||||
private val state = MutableStateFlow(
|
||||
MessageAvailabilityData(
|
||||
isAppInForeground = true,
|
||||
isThirdPartyUiShown = false,
|
||||
hasMessageBeenShown = false
|
||||
)
|
||||
)
|
||||
|
||||
override val canShowMessage: Boolean
|
||||
get() = state.value
|
||||
get() = state.value.canShowMessage
|
||||
|
||||
init {
|
||||
scope.launch {
|
||||
applicationStateProvider.state.collect {
|
||||
if (it == Lifecycle.Event.ON_START) {
|
||||
state.update { true }
|
||||
applicationStateProvider.state
|
||||
.onEach { event ->
|
||||
if (event == Lifecycle.Event.ON_START) {
|
||||
state.update {
|
||||
it.copy(
|
||||
isAppInForeground = true,
|
||||
hasMessageBeenShown = if (it.isThirdPartyUiShown) it.hasMessageBeenShown else false,
|
||||
isThirdPartyUiShown = false
|
||||
)
|
||||
}
|
||||
} else if (event == Lifecycle.Event.ON_STOP) {
|
||||
state.update {
|
||||
it.copy(
|
||||
isAppInForeground = it.isThirdPartyUiShown,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.launchIn(scope)
|
||||
}
|
||||
|
||||
override fun observe(): StateFlow<Boolean> = state.asStateFlow()
|
||||
override fun observe(): Flow<Boolean> = state.map { it.canShowMessage }.distinctUntilChanged()
|
||||
|
||||
override fun onMessageShown() {
|
||||
state.update { false }
|
||||
state.update { it.copy(hasMessageBeenShown = true) }
|
||||
}
|
||||
|
||||
override fun onThirdPartyUiShown() {
|
||||
state.update { it.copy(isThirdPartyUiShown = true) }
|
||||
}
|
||||
}
|
||||
|
||||
private data class MessageAvailabilityData(
|
||||
val isAppInForeground: Boolean,
|
||||
val isThirdPartyUiShown: Boolean,
|
||||
val hasMessageBeenShown: Boolean
|
||||
) {
|
||||
val canShowMessage = isAppInForeground && !hasMessageBeenShown
|
||||
}
|
||||
|
|
|
@ -10,12 +10,7 @@ import co.electriccoin.zcash.ui.common.viewmodel.SynchronizerError
|
|||
// TODO [#292]: Should be moved to SDK-EXT-UI module.
|
||||
// TODO [#292]: https://github.com/Electric-Coin-Company/zashi-android/issues/292
|
||||
data class WalletSnapshot(
|
||||
val isZashi: Boolean,
|
||||
val status: Synchronizer.Status,
|
||||
val processorInfo: CompactBlockProcessor.ProcessorInfo,
|
||||
val orchardBalance: WalletBalance,
|
||||
val saplingBalance: WalletBalance?,
|
||||
val transparentBalance: Zatoshi,
|
||||
val progress: PercentDecimal,
|
||||
val synchronizerError: SynchronizerError?
|
||||
)
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
package co.electriccoin.zcash.ui.common.repository
|
||||
|
||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
import co.electriccoin.zcash.ui.common.datasource.MessageAvailabilityDataSource
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.SynchronizerError
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
interface HomeMessageCacheRepository {
|
||||
/**
|
||||
* Last message that was shown. Null if no message has been shown yet.
|
||||
*/
|
||||
var lastShownMessage: HomeMessageData?
|
||||
|
||||
/**
|
||||
* Last message that was shown. Null if no message has been shown yet or if last message was null.
|
||||
*/
|
||||
var lastMessage: HomeMessageData?
|
||||
|
||||
fun init()
|
||||
|
||||
fun reset()
|
||||
}
|
||||
|
||||
class HomeMessageCacheRepositoryImpl(
|
||||
private val messageAvailabilityDataSource: MessageAvailabilityDataSource
|
||||
) : HomeMessageCacheRepository {
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
|
||||
|
||||
override var lastShownMessage: HomeMessageData? = null
|
||||
override var lastMessage: HomeMessageData? = null
|
||||
|
||||
override fun init() {
|
||||
messageAvailabilityDataSource
|
||||
.observe()
|
||||
.onEach { canShowMessage ->
|
||||
if (canShowMessage) {
|
||||
lastShownMessage = null
|
||||
lastMessage = null
|
||||
}
|
||||
}
|
||||
.launchIn(scope)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
lastShownMessage = null
|
||||
lastMessage = null
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface HomeMessageData {
|
||||
|
||||
val priority: Int
|
||||
|
||||
data class Error(val synchronizerError: SynchronizerError) : RuntimeMessage()
|
||||
data object Disconnected : RuntimeMessage()
|
||||
data class Restoring(val progress: Float) : RuntimeMessage()
|
||||
data class Syncing(val progress: Float) : RuntimeMessage()
|
||||
data object Updating : RuntimeMessage()
|
||||
|
||||
data object Backup : Prioritized {
|
||||
override val priority: Int = 3
|
||||
}
|
||||
|
||||
data class ShieldFunds(val zatoshi: Zatoshi) : Prioritized {
|
||||
override val priority: Int = 2
|
||||
}
|
||||
|
||||
data object EnableCurrencyConversion : Prioritized {
|
||||
override val priority: Int = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Message which always is shown.
|
||||
*/
|
||||
sealed class RuntimeMessage : HomeMessageData {
|
||||
override val priority: Int = 4
|
||||
}
|
||||
|
||||
/**
|
||||
* Message which always is displayed only if previous message was lower priority.
|
||||
*/
|
||||
sealed interface Prioritized : HomeMessageData
|
|
@ -1,5 +1,7 @@
|
|||
package co.electriccoin.zcash.ui.common.repository
|
||||
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.model.AccountUuid
|
||||
import cash.z.ecc.android.sdk.model.TransactionId
|
||||
import cash.z.ecc.android.sdk.model.TransactionOutput
|
||||
import cash.z.ecc.android.sdk.model.TransactionOverview
|
||||
|
@ -8,6 +10,7 @@ import cash.z.ecc.android.sdk.model.TransactionState.Expired
|
|||
import cash.z.ecc.android.sdk.model.TransactionState.Pending
|
||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
import co.electriccoin.zcash.ui.common.datasource.AccountDataSource
|
||||
import co.electriccoin.zcash.ui.common.model.ZashiAccount
|
||||
import co.electriccoin.zcash.ui.common.provider.SynchronizerProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -20,6 +23,7 @@ import kotlinx.coroutines.flow.WhileSubscribed
|
|||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
|
@ -38,6 +42,8 @@ import kotlin.time.Duration
|
|||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
interface TransactionRepository {
|
||||
val zashiTransactions: Flow<List<Transaction>?>
|
||||
|
||||
val currentTransactions: Flow<List<Transaction>?>
|
||||
|
||||
suspend fun getMemos(transaction: Transaction): List<String>
|
||||
|
@ -52,21 +58,49 @@ interface TransactionRepository {
|
|||
}
|
||||
|
||||
class TransactionRepositoryImpl(
|
||||
accountDataSource: AccountDataSource,
|
||||
private val accountDataSource: AccountDataSource,
|
||||
private val synchronizerProvider: SynchronizerProvider,
|
||||
) : TransactionRepository {
|
||||
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
|
||||
|
||||
override val zashiTransactions: Flow<List<Transaction>?> =
|
||||
observeTransactions(
|
||||
accountFlow = accountDataSource.zashiAccount.map { it?.sdkAccount?.accountUuid }.distinctUntilChanged()
|
||||
).stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.WhileSubscribed(5.seconds, Duration.ZERO),
|
||||
initialValue = null
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override val currentTransactions: Flow<List<Transaction>?> =
|
||||
override val currentTransactions: Flow<List<Transaction>?> = accountDataSource.selectedAccount
|
||||
.distinctUntilChangedBy { it?.sdkAccount?.accountUuid }
|
||||
.flatMapLatest { selected ->
|
||||
if (selected is ZashiAccount) {
|
||||
zashiTransactions
|
||||
} else {
|
||||
observeTransactions(
|
||||
accountFlow = accountDataSource.selectedAccount.map { it?.sdkAccount?.accountUuid }
|
||||
.distinctUntilChanged()
|
||||
)
|
||||
}
|
||||
}
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.WhileSubscribed(5.seconds, Duration.ZERO),
|
||||
initialValue = null
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private fun TransactionRepositoryImpl.observeTransactions(accountFlow: Flow<AccountUuid?>) =
|
||||
combine(
|
||||
synchronizerProvider.synchronizer,
|
||||
accountDataSource.selectedAccount.map { it?.sdkAccount }
|
||||
accountFlow
|
||||
) { synchronizer, account ->
|
||||
synchronizer to account
|
||||
}.distinctUntilChanged()
|
||||
.flatMapLatest { (synchronizer, account) ->
|
||||
if (synchronizer == null || account == null) {
|
||||
.flatMapLatest { (synchronizer, accountUuid) ->
|
||||
if (synchronizer == null || accountUuid == null) {
|
||||
flowOf(null)
|
||||
} else {
|
||||
channelFlow<List<Transaction>?> {
|
||||
|
@ -74,144 +108,9 @@ class TransactionRepositoryImpl(
|
|||
|
||||
launch {
|
||||
synchronizer
|
||||
.getTransactions(account.accountUuid)
|
||||
.getTransactions(accountUuid)
|
||||
.mapLatest { transactions ->
|
||||
transactions
|
||||
.map { transaction ->
|
||||
when (transaction.transactionState) {
|
||||
Expired ->
|
||||
when {
|
||||
transaction.isShielding ->
|
||||
ShieldTransaction.Failed(
|
||||
timestamp =
|
||||
createTimestamp(transaction) ?: Instant.now(),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs
|
||||
(transaction),
|
||||
amount = transaction.totalSpent,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
fee = transaction.netValue,
|
||||
overview = transaction
|
||||
)
|
||||
|
||||
transaction.isSentTransaction ->
|
||||
SendTransaction.Failed(
|
||||
timestamp =
|
||||
createTimestamp(transaction) ?: Instant.now(),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs
|
||||
(transaction),
|
||||
amount = transaction.netValue,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
fee = transaction.feePaid,
|
||||
overview = transaction
|
||||
)
|
||||
|
||||
else ->
|
||||
ReceiveTransaction.Failed(
|
||||
timestamp =
|
||||
createTimestamp(transaction) ?: Instant.now(),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs(transaction),
|
||||
amount = transaction.netValue,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
overview = transaction
|
||||
)
|
||||
}
|
||||
|
||||
Confirmed ->
|
||||
when {
|
||||
transaction.isShielding ->
|
||||
ShieldTransaction.Success(
|
||||
timestamp =
|
||||
createTimestamp(transaction) ?: Instant.now(),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs
|
||||
(transaction),
|
||||
amount = transaction.totalSpent,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
fee = transaction.netValue,
|
||||
overview = transaction
|
||||
)
|
||||
|
||||
transaction.isSentTransaction ->
|
||||
SendTransaction.Success(
|
||||
timestamp =
|
||||
createTimestamp(transaction) ?: Instant.now(),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs
|
||||
(transaction),
|
||||
amount = transaction.netValue,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
fee = transaction.feePaid,
|
||||
overview = transaction
|
||||
)
|
||||
|
||||
else ->
|
||||
ReceiveTransaction.Success(
|
||||
timestamp =
|
||||
createTimestamp(transaction) ?: Instant.now(),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs
|
||||
(transaction),
|
||||
amount = transaction.netValue,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
overview = transaction
|
||||
)
|
||||
}
|
||||
|
||||
Pending ->
|
||||
when {
|
||||
transaction.isShielding ->
|
||||
ShieldTransaction.Pending(
|
||||
timestamp = createTimestamp(transaction),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs
|
||||
(transaction),
|
||||
amount = transaction.totalSpent,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
fee = transaction.netValue,
|
||||
overview = transaction
|
||||
)
|
||||
|
||||
transaction.isSentTransaction ->
|
||||
SendTransaction.Pending(
|
||||
timestamp = createTimestamp(transaction),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs
|
||||
(transaction),
|
||||
amount = transaction.netValue,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
fee = transaction.feePaid,
|
||||
overview = transaction
|
||||
)
|
||||
|
||||
else ->
|
||||
ReceiveTransaction.Pending(
|
||||
timestamp = createTimestamp(transaction),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs
|
||||
(transaction),
|
||||
amount = transaction.netValue,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
overview = transaction
|
||||
)
|
||||
}
|
||||
|
||||
else -> error("Unexpected transaction stat")
|
||||
}
|
||||
}.sortedByDescending { transaction ->
|
||||
transaction.timestamp ?: Instant.now()
|
||||
}
|
||||
createTransactions(transactions = transactions, synchronizer = synchronizer)
|
||||
}.collect {
|
||||
send(it)
|
||||
}
|
||||
|
@ -222,11 +121,147 @@ class TransactionRepositoryImpl(
|
|||
}
|
||||
}
|
||||
}
|
||||
}.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.WhileSubscribed(5.seconds, Duration.ZERO),
|
||||
initialValue = null
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun createTransactions(
|
||||
transactions: List<TransactionOverview>,
|
||||
synchronizer: Synchronizer
|
||||
) = transactions
|
||||
.map { transaction ->
|
||||
when (transaction.transactionState) {
|
||||
Expired ->
|
||||
when {
|
||||
transaction.isShielding ->
|
||||
ShieldTransaction.Failed(
|
||||
timestamp =
|
||||
createTimestamp(transaction) ?: Instant.now(),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs
|
||||
(transaction),
|
||||
amount = transaction.totalSpent,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
fee = transaction.netValue,
|
||||
overview = transaction
|
||||
)
|
||||
|
||||
transaction.isSentTransaction ->
|
||||
SendTransaction.Failed(
|
||||
timestamp =
|
||||
createTimestamp(transaction) ?: Instant.now(),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs
|
||||
(transaction),
|
||||
amount = transaction.netValue,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
fee = transaction.feePaid,
|
||||
overview = transaction
|
||||
)
|
||||
|
||||
else ->
|
||||
ReceiveTransaction.Failed(
|
||||
timestamp =
|
||||
createTimestamp(transaction) ?: Instant.now(),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs(transaction),
|
||||
amount = transaction.netValue,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
overview = transaction
|
||||
)
|
||||
}
|
||||
|
||||
Confirmed ->
|
||||
when {
|
||||
transaction.isShielding ->
|
||||
ShieldTransaction.Success(
|
||||
timestamp =
|
||||
createTimestamp(transaction) ?: Instant.now(),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs
|
||||
(transaction),
|
||||
amount = transaction.totalSpent,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
fee = transaction.netValue,
|
||||
overview = transaction
|
||||
)
|
||||
|
||||
transaction.isSentTransaction ->
|
||||
SendTransaction.Success(
|
||||
timestamp =
|
||||
createTimestamp(transaction) ?: Instant.now(),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs
|
||||
(transaction),
|
||||
amount = transaction.netValue,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
fee = transaction.feePaid,
|
||||
overview = transaction
|
||||
)
|
||||
|
||||
else ->
|
||||
ReceiveTransaction.Success(
|
||||
timestamp =
|
||||
createTimestamp(transaction) ?: Instant.now(),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs
|
||||
(transaction),
|
||||
amount = transaction.netValue,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
overview = transaction
|
||||
)
|
||||
}
|
||||
|
||||
Pending ->
|
||||
when {
|
||||
transaction.isShielding ->
|
||||
ShieldTransaction.Pending(
|
||||
timestamp = createTimestamp(transaction),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs
|
||||
(transaction),
|
||||
amount = transaction.totalSpent,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
fee = transaction.netValue,
|
||||
overview = transaction
|
||||
)
|
||||
|
||||
transaction.isSentTransaction ->
|
||||
SendTransaction.Pending(
|
||||
timestamp = createTimestamp(transaction),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs
|
||||
(transaction),
|
||||
amount = transaction.netValue,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
fee = transaction.feePaid,
|
||||
overview = transaction
|
||||
)
|
||||
|
||||
else ->
|
||||
ReceiveTransaction.Pending(
|
||||
timestamp = createTimestamp(transaction),
|
||||
transactionOutputs =
|
||||
synchronizer.getTransactionOutputs
|
||||
(transaction),
|
||||
amount = transaction.netValue,
|
||||
id = transaction.txId,
|
||||
memoCount = transaction.memoCount,
|
||||
overview = transaction
|
||||
)
|
||||
}
|
||||
|
||||
else -> error("Unexpected transaction stat")
|
||||
}
|
||||
}.sortedByDescending { transaction ->
|
||||
transaction.timestamp ?: Instant.now()
|
||||
}
|
||||
|
||||
private fun createTimestamp(transaction: TransactionOverview): Instant? =
|
||||
transaction.blockTimeEpochSeconds?.let {
|
||||
|
|
|
@ -4,7 +4,6 @@ import android.app.Application
|
|||
import cash.z.ecc.android.sdk.SdkSynchronizer
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.WalletInitMode
|
||||
import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
import cash.z.ecc.android.sdk.model.FastestServersResult
|
||||
import cash.z.ecc.android.sdk.model.PercentDecimal
|
||||
|
@ -23,7 +22,6 @@ import co.electriccoin.zcash.ui.common.model.OnboardingState
|
|||
import co.electriccoin.zcash.ui.common.model.WalletAccount
|
||||
import co.electriccoin.zcash.ui.common.model.WalletRestoringState
|
||||
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
|
||||
import co.electriccoin.zcash.ui.common.model.ZashiAccount
|
||||
import co.electriccoin.zcash.ui.common.provider.GetDefaultServersProvider
|
||||
import co.electriccoin.zcash.ui.common.provider.PersistableWalletProvider
|
||||
import co.electriccoin.zcash.ui.common.provider.SynchronizerProvider
|
||||
|
@ -143,6 +141,7 @@ class WalletRepositoryImpl(
|
|||
OnboardingState.NEEDS_WARN,
|
||||
OnboardingState.NEEDS_BACKUP,
|
||||
OnboardingState.NONE -> SecretState.NONE
|
||||
|
||||
OnboardingState.READY -> SecretState.READY
|
||||
}
|
||||
}
|
||||
|
@ -190,13 +189,11 @@ class WalletRepositoryImpl(
|
|||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override val currentWalletSnapshot: StateFlow<WalletSnapshot?> =
|
||||
combine(synchronizer, currentAccount) { synchronizer, currentAccount ->
|
||||
synchronizer to currentAccount
|
||||
}.flatMapLatest { (synchronizer, currentAccount) ->
|
||||
if (synchronizer == null || currentAccount == null) {
|
||||
synchronizer.flatMapLatest { synchronizer ->
|
||||
if (synchronizer == null) {
|
||||
flowOf(null)
|
||||
} else {
|
||||
toWalletSnapshot(synchronizer, currentAccount)
|
||||
toWalletSnapshot(synchronizer)
|
||||
}
|
||||
}.stateIn(
|
||||
scope = scope,
|
||||
|
@ -355,29 +352,14 @@ private fun Synchronizer.toCommonError(): Flow<SynchronizerError?> =
|
|||
|
||||
// No good way around needing magic numbers for the indices
|
||||
@Suppress("MagicNumber")
|
||||
private fun toWalletSnapshot(
|
||||
synchronizer: Synchronizer,
|
||||
account: WalletAccount
|
||||
) = combine(
|
||||
// 0
|
||||
synchronizer.status,
|
||||
// 1
|
||||
synchronizer.processorInfo,
|
||||
// 2
|
||||
synchronizer.progress,
|
||||
// 3
|
||||
synchronizer.toCommonError()
|
||||
private fun toWalletSnapshot(synchronizer: Synchronizer) = combine(
|
||||
synchronizer.status, // 0
|
||||
synchronizer.progress, // 1
|
||||
synchronizer.toCommonError() // 2
|
||||
) { flows ->
|
||||
val progressPercentDecimal = (flows[2] as PercentDecimal)
|
||||
|
||||
WalletSnapshot(
|
||||
isZashi = account is ZashiAccount,
|
||||
status = flows[0] as Synchronizer.Status,
|
||||
processorInfo = flows[1] as CompactBlockProcessor.ProcessorInfo,
|
||||
orchardBalance = account.unified.balance,
|
||||
saplingBalance = account.sapling?.balance,
|
||||
transparentBalance = account.transparent.balance,
|
||||
progress = progressPercentDecimal,
|
||||
synchronizerError = flows[3] as SynchronizerError?
|
||||
progress = flows[1] as PercentDecimal,
|
||||
synchronizerError = flows[2] as SynchronizerError?
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
package co.electriccoin.zcash.ui.common.usecase
|
||||
|
||||
import android.util.Log
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
import co.electriccoin.zcash.spackle.Twig
|
||||
import co.electriccoin.zcash.ui.common.datasource.MessageAvailabilityDataSource
|
||||
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.model.WalletSnapshot
|
||||
import co.electriccoin.zcash.ui.common.repository.ExchangeRateRepository
|
||||
import co.electriccoin.zcash.ui.common.repository.HomeMessageCacheRepository
|
||||
import co.electriccoin.zcash.ui.common.repository.HomeMessageData
|
||||
import co.electriccoin.zcash.ui.common.repository.ReceiveTransaction
|
||||
import co.electriccoin.zcash.ui.common.repository.RuntimeMessage
|
||||
import co.electriccoin.zcash.ui.common.repository.ShieldFundsData
|
||||
import co.electriccoin.zcash.ui.common.repository.ShieldFundsRepository
|
||||
import co.electriccoin.zcash.ui.common.repository.TransactionRepository
|
||||
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 kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
@ -18,69 +25,117 @@ import kotlinx.coroutines.flow.debounce
|
|||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class GetHomeMessageUseCase(
|
||||
private val walletRepository: WalletRepository,
|
||||
private val walletBackupDataSource: WalletBackupDataSource,
|
||||
private val exchangeRateRepository: ExchangeRateRepository,
|
||||
private val shieldFundsRepository: ShieldFundsRepository,
|
||||
walletRepository: WalletRepository,
|
||||
walletBackupDataSource: WalletBackupDataSource,
|
||||
exchangeRateRepository: ExchangeRateRepository,
|
||||
shieldFundsRepository: ShieldFundsRepository,
|
||||
transactionRepository: TransactionRepository,
|
||||
private val messageAvailabilityDataSource: MessageAvailabilityDataSource,
|
||||
private val cache: HomeMessageCacheRepository,
|
||||
) {
|
||||
private val backupFlow = combine(
|
||||
transactionRepository.zashiTransactions,
|
||||
walletBackupDataSource.observe()
|
||||
) { transactions, backup ->
|
||||
if (backup is WalletBackupAvailability.Available && transactions.orEmpty().any { it is ReceiveTransaction }) {
|
||||
backup
|
||||
} else {
|
||||
WalletBackupAvailability.Unavailable
|
||||
}
|
||||
}.distinctUntilChanged()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
fun observe(): Flow<HomeMessageData?> = combine(
|
||||
private val flow = combine(
|
||||
walletRepository.currentWalletSnapshot.filterNotNull(),
|
||||
walletRepository.walletRestoringState,
|
||||
walletBackupDataSource.observe(),
|
||||
walletRepository.walletRestoringState.filterNotNull(),
|
||||
backupFlow,
|
||||
exchangeRateRepository.state.map { it == ExchangeRateState.OptIn }.distinctUntilChanged(),
|
||||
shieldFundsRepository.availability
|
||||
) { walletSnapshot, walletStateInformation, backup, isCCAvailable, shieldFunds ->
|
||||
when {
|
||||
walletSnapshot.synchronizerError != null -> {
|
||||
HomeMessageData.Error(walletSnapshot.synchronizerError)
|
||||
}
|
||||
createMessage(walletSnapshot, walletStateInformation, backup, shieldFunds, isCCAvailable)
|
||||
}.debounce(.5.seconds)
|
||||
.map { message -> prioritizeMessage(message) }
|
||||
|
||||
walletSnapshot.status == Synchronizer.Status.DISCONNECTED -> {
|
||||
HomeMessageData.Disconnected
|
||||
}
|
||||
fun observe(): Flow<HomeMessageData?> = flow
|
||||
|
||||
walletSnapshot.status in listOf(
|
||||
Synchronizer.Status.INITIALIZING,
|
||||
Synchronizer.Status.SYNCING,
|
||||
Synchronizer.Status.STOPPED
|
||||
) -> {
|
||||
val progress = walletSnapshot.progress.decimal * 100f
|
||||
val result = when {
|
||||
walletStateInformation == WalletRestoringState.RESTORING -> {
|
||||
HomeMessageData.Restoring(
|
||||
progress = progress,
|
||||
)
|
||||
}
|
||||
private fun createMessage(
|
||||
walletSnapshot: WalletSnapshot,
|
||||
walletStateInformation: WalletRestoringState,
|
||||
backup: WalletBackupAvailability,
|
||||
shieldFunds: ShieldFundsData,
|
||||
isCCAvailable: Boolean
|
||||
) = when {
|
||||
walletSnapshot.synchronizerError != null -> HomeMessageData.Error(walletSnapshot.synchronizerError)
|
||||
|
||||
else -> {
|
||||
HomeMessageData.Syncing(progress = progress)
|
||||
}
|
||||
walletSnapshot.status == Synchronizer.Status.DISCONNECTED -> HomeMessageData.Disconnected
|
||||
|
||||
walletSnapshot.status in listOf(
|
||||
Synchronizer.Status.INITIALIZING,
|
||||
Synchronizer.Status.SYNCING,
|
||||
Synchronizer.Status.STOPPED
|
||||
) -> {
|
||||
val progress = walletSnapshot.progress.decimal * 100f
|
||||
val result = when {
|
||||
walletStateInformation == WalletRestoringState.RESTORING -> {
|
||||
HomeMessageData.Restoring(
|
||||
progress = progress,
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
HomeMessageData.Syncing(progress = progress)
|
||||
}
|
||||
result
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
shieldFunds is ShieldFundsData.Available -> HomeMessageData.ShieldFunds(shieldFunds.amount)
|
||||
backup is WalletBackupAvailability.Available -> HomeMessageData.Backup
|
||||
|
||||
backup is WalletBackupAvailability.Available -> HomeMessageData.Backup
|
||||
shieldFunds is ShieldFundsData.Available -> HomeMessageData.ShieldFunds(shieldFunds.amount)
|
||||
|
||||
isCCAvailable -> HomeMessageData.EnableCurrencyConversion
|
||||
isCCAvailable -> HomeMessageData.EnableCurrencyConversion
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun prioritizeMessage(message: HomeMessageData?): HomeMessageData? {
|
||||
val isSameMessageUpdate = message?.priority == cache.lastMessage?.priority // same but updated
|
||||
val someMessageBeenShown = cache.lastShownMessage != null // has any message been shown while app in fg
|
||||
val hasNoMessageBeenShownLately = cache.lastMessage == null // has no message been shown
|
||||
val isHigherPriorityMessage = (message?.priority ?: 0) > (cache.lastShownMessage?.priority ?: 0)
|
||||
val result = when {
|
||||
message == null -> null
|
||||
message is RuntimeMessage -> message
|
||||
isSameMessageUpdate -> message
|
||||
isHigherPriorityMessage -> if (hasNoMessageBeenShownLately) {
|
||||
if (someMessageBeenShown) null else message
|
||||
} else {
|
||||
message
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
}.debounce(.5.seconds)
|
||||
}
|
||||
|
||||
sealed interface HomeMessageData {
|
||||
data object EnableCurrencyConversion : HomeMessageData
|
||||
data class ShieldFunds(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
|
||||
if (result != null) {
|
||||
messageAvailabilityDataSource.onMessageShown()
|
||||
cache.lastShownMessage = result
|
||||
}
|
||||
cache.lastMessage = result
|
||||
|
||||
Twig.debug {
|
||||
when {
|
||||
message == null -> "Home message: no message to show"
|
||||
result == null -> "Home message: ${message::class.simpleName} was filtered out"
|
||||
else -> {
|
||||
"Home message: ${result::class.simpleName} shown"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package co.electriccoin.zcash.ui.common.usecase
|
||||
|
||||
import co.electriccoin.zcash.ui.common.repository.AddressBookRepository
|
||||
import co.electriccoin.zcash.ui.common.repository.HomeMessageCacheRepository
|
||||
|
||||
class ResetInMemoryDataUseCase(
|
||||
private val addressBookRepository: AddressBookRepository,
|
||||
private val homeMessageCacheRepository: HomeMessageCacheRepository
|
||||
) {
|
||||
suspend operator fun invoke() {
|
||||
addressBookRepository.resetAddressBook()
|
||||
homeMessageCacheRepository.reset()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
package co.electriccoin.zcash.ui.fixture
|
||||
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor
|
||||
import cash.z.ecc.android.sdk.fixture.WalletBalanceFixture
|
||||
import cash.z.ecc.android.sdk.model.PercentDecimal
|
||||
import cash.z.ecc.android.sdk.model.WalletBalance
|
||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
import cash.z.ecc.sdk.fixture.ZatoshiFixture
|
||||
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.SynchronizerError
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
object WalletSnapshotFixture {
|
||||
val STATUS = Synchronizer.Status.SYNCED
|
||||
val PROGRESS = PercentDecimal.ZERO_PERCENT
|
||||
val TRANSPARENT_BALANCE: Zatoshi = ZatoshiFixture.new(8)
|
||||
val ORCHARD_BALANCE: WalletBalance = WalletBalanceFixture.newLong(8, 2, 1)
|
||||
val SAPLING_BALANCE: WalletBalance = WalletBalanceFixture.newLong(5, 2, 1)
|
||||
|
||||
// Should fill in with non-empty values for better example values in tests and UI previews
|
||||
@Suppress("LongParameterList")
|
||||
fun new(
|
||||
status: Synchronizer.Status = STATUS,
|
||||
processorInfo: CompactBlockProcessor.ProcessorInfo =
|
||||
CompactBlockProcessor.ProcessorInfo(
|
||||
null,
|
||||
null,
|
||||
null
|
||||
),
|
||||
orchardBalance: WalletBalance = ORCHARD_BALANCE,
|
||||
saplingBalance: WalletBalance = SAPLING_BALANCE,
|
||||
transparentBalance: Zatoshi = TRANSPARENT_BALANCE,
|
||||
progress: PercentDecimal = PROGRESS,
|
||||
synchronizerError: SynchronizerError? = null
|
||||
) = WalletSnapshot(
|
||||
status = status,
|
||||
processorInfo = processorInfo,
|
||||
orchardBalance = orchardBalance,
|
||||
saplingBalance = saplingBalance,
|
||||
transparentBalance = transparentBalance,
|
||||
progress = progress,
|
||||
synchronizerError = synchronizerError,
|
||||
isZashi = false,
|
||||
)
|
||||
}
|
|
@ -12,7 +12,7 @@ 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.repository.HomeMessageData
|
||||
import co.electriccoin.zcash.ui.common.usecase.IsRestoreSuccessDialogVisibleUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.NavigateToCoinbaseUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ShieldFundsUseCase
|
||||
|
@ -246,6 +246,6 @@ class HomeViewModel(
|
|||
// ),
|
||||
// fullStackTrace = walletSnapshot.synchronizerError.getStackTrace(limit = null)
|
||||
// )
|
||||
TODO()
|
||||
// TODO()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import co.electriccoin.zcash.ui.Navigator
|
|||
import co.electriccoin.zcash.ui.NavigatorImpl
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalActivity
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalNavController
|
||||
import co.electriccoin.zcash.ui.common.datasource.MessageAvailabilityDataSource
|
||||
import co.electriccoin.zcash.ui.common.model.OnboardingState
|
||||
import co.electriccoin.zcash.ui.common.model.WalletRestoringState
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
|
||||
|
@ -50,25 +51,29 @@ import org.koin.compose.koinInject
|
|||
@Composable
|
||||
fun MainActivity.OnboardingNavigation() {
|
||||
val activity = LocalActivity.current
|
||||
val navigationRouter = koinInject<NavigationRouter>()
|
||||
val navController = LocalNavController.current
|
||||
val keyboardManager = LocalKeyboardManager.current
|
||||
val flexaViewModel = koinViewModel<FlexaViewModel>()
|
||||
val sheetStateManager = LocalSheetStateManager.current
|
||||
val navigationRouter = koinInject<NavigationRouter>()
|
||||
val flexaViewModel = koinViewModel<FlexaViewModel>()
|
||||
val messageAvailabilityDataSource = koinInject<MessageAvailabilityDataSource>()
|
||||
|
||||
|
||||
val navigator: Navigator =
|
||||
remember(
|
||||
navController,
|
||||
flexaViewModel,
|
||||
keyboardManager,
|
||||
sheetStateManager
|
||||
sheetStateManager,
|
||||
messageAvailabilityDataSource
|
||||
) {
|
||||
NavigatorImpl(
|
||||
activity = this@OnboardingNavigation,
|
||||
navController = navController,
|
||||
flexaViewModel = flexaViewModel,
|
||||
keyboardManager = keyboardManager,
|
||||
sheetStateManager = sheetStateManager
|
||||
sheetStateManager = sheetStateManager,
|
||||
messageAvailabilityDataSource = messageAvailabilityDataSource
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -77,8 +77,7 @@ class TransactionHistoryWidgetViewModel(
|
|||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
||||
initialValue =
|
||||
TransactionHistoryWidgetState.Loading
|
||||
initialValue = TransactionHistoryWidgetState.Loading
|
||||
)
|
||||
|
||||
private fun onTransactionClick(transaction: Transaction) {
|
||||
|
|
Loading…
Reference in New Issue