Navigation refactor - NavigationRouter added (#1698)

This commit is contained in:
Milan 2024-12-02 13:32:47 +01:00 committed by GitHub
parent 8a77a38133
commit f60ba7597b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 131 additions and 268 deletions

View File

@ -7,6 +7,8 @@ import co.electriccoin.zcash.global.newInstance
import co.electriccoin.zcash.preference.EncryptedPreferenceProvider
import co.electriccoin.zcash.preference.StandardPreferenceProvider
import co.electriccoin.zcash.preference.model.entry.PreferenceKey
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.NavigationRouterImpl
import co.electriccoin.zcash.ui.preference.PersistableWalletPreferenceDefault
import co.electriccoin.zcash.ui.screen.update.AppUpdateChecker
import co.electriccoin.zcash.ui.screen.update.AppUpdateCheckerImpl
@ -37,4 +39,6 @@ val coreModule =
factoryOf(::AppUpdateCheckerImpl) bind AppUpdateChecker::class
factory { AndroidConfigurationFactory.new() }
singleOf(::NavigationRouterImpl) bind NavigationRouter::class
}

View File

@ -67,6 +67,7 @@ val viewModelModule =
args = args,
observeAddressBookContacts = get(),
observeContactPicked = get(),
navigationRouter = get()
)
}
viewModel { (address: String?) ->
@ -75,6 +76,7 @@ val viewModelModule =
validateContactAddress = get(),
validateContactName = get(),
saveContact = get(),
navigationRouter = get()
)
}
viewModelOf(::UpdateContactViewModel)

View File

@ -10,6 +10,7 @@ import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
import androidx.navigation.NavOptionsBuilder
import androidx.navigation.NavType
@ -101,6 +102,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
import org.koin.compose.koinInject
// TODO [#1297]: Consider: Navigation passing complex data arguments different way
// TODO [#1297]: https://github.com/Electric-Coin-Company/zashi-android/issues/1297
@ -117,16 +119,21 @@ internal fun MainActivity.Navigation() {
rememberSaveable { mutableStateOf(false) }
val (deleteWalletAuthentication, setDeleteWalletAuthentication) =
rememberSaveable { mutableStateOf(false) }
val navigationRouter = koinInject<NavigationRouter>()
LaunchedEffect(Unit) {
walletViewModel.navigationCommand.collect {
navController.navigateJustOnce(it)
}
}
LaunchedEffect(Unit) {
walletViewModel.backNavigationCommand.collect {
navController.popBackStack()
navigationRouter.observe().collect {
when (it) {
is NavigationCommand.Forward -> navController.navigate(it.route)
is NavigationCommand.Replace ->
navController.navigate(it.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
restoreState = true
}
NavigationCommand.Back -> navController.popBackStack()
}
}
}

View File

@ -0,0 +1,53 @@
package co.electriccoin.zcash.ui
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.launch
interface NavigationRouter {
fun forward(route: String)
fun replace(route: String)
fun back()
fun observe(): Flow<NavigationCommand>
}
class NavigationRouterImpl : NavigationRouter {
private val scope = CoroutineScope(Dispatchers.Main.immediate + SupervisorJob())
private val channel = Channel<NavigationCommand>()
override fun forward(route: String) {
scope.launch {
channel.send(NavigationCommand.Forward(route))
}
}
override fun replace(route: String) {
scope.launch {
channel.send(NavigationCommand.Replace(route))
}
}
override fun back() {
scope.launch {
channel.send(NavigationCommand.Back)
}
}
override fun observe() = channel.consumeAsFlow()
}
sealed interface NavigationCommand {
data class Forward(val route: String) : NavigationCommand
data class Replace(val route: String) : NavigationCommand
data object Back : NavigationCommand
}

View File

@ -5,6 +5,7 @@ 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.NavigationTargets.EXCHANGE_RATE_OPT_IN
import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState
import co.electriccoin.zcash.ui.common.wallet.RefreshLock
@ -15,7 +16,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.WhileSubscribed
@ -36,10 +36,6 @@ import kotlinx.coroutines.launch
import kotlin.time.Duration.Companion.minutes
interface ExchangeRateRepository {
val navigationCommand: MutableSharedFlow<String>
val backNavigationCommand: MutableSharedFlow<Unit>
val isExchangeRateUsdOptedIn: StateFlow<Boolean?>
val state: StateFlow<ExchangeRateState>
@ -54,6 +50,7 @@ interface ExchangeRateRepository {
class ExchangeRateRepositoryImpl(
private val walletRepository: WalletRepository,
private val standardPreferenceProvider: StandardPreferenceProvider,
private val navigationRouter: NavigationRouter,
) : ExchangeRateRepository {
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
@ -164,10 +161,6 @@ class ExchangeRateRepositoryImpl(
initialValue = ExchangeRateState.OptedOut
)
override val navigationCommand = MutableSharedFlow<String>()
override val backNavigationCommand = MutableSharedFlow<Unit>()
override fun refreshExchangeRateUsd() {
refreshExchangeRateUsdInternal()
}
@ -182,27 +175,20 @@ class ExchangeRateRepositoryImpl(
}
override fun optInExchangeRateUsd(optIn: Boolean) {
scope.launch {
setNullableBooleanPreference(StandardPreferenceKeys.EXCHANGE_RATE_OPTED_IN, optIn)
backNavigationCommand.emit(Unit)
}
setNullableBooleanPreference(StandardPreferenceKeys.EXCHANGE_RATE_OPTED_IN, optIn)
navigationRouter.back()
}
override fun dismissOptInExchangeRateUsd() {
scope.launch {
setNullableBooleanPreference(StandardPreferenceKeys.EXCHANGE_RATE_OPTED_IN, false)
backNavigationCommand.emit(Unit)
}
setNullableBooleanPreference(StandardPreferenceKeys.EXCHANGE_RATE_OPTED_IN, false)
navigationRouter.back()
}
private fun dismissWidgetOptInExchangeRateUsd() {
setNullableBooleanPreference(StandardPreferenceKeys.EXCHANGE_RATE_OPTED_IN, false)
}
private fun showOptInExchangeRateUsd() =
scope.launch {
navigationCommand.emit(EXCHANGE_RATE_OPT_IN)
}
private fun showOptInExchangeRateUsd() = navigationRouter.forward(EXCHANGE_RATE_OPT_IN)
private fun nullableBooleanStateFlow(default: NullableBooleanPreferenceDefault): StateFlow<Boolean?> =
flow {

View File

@ -68,10 +68,6 @@ class WalletViewModel(
private val isFlexaAvailable: IsFlexaAvailableUseCase,
private val getSynchronizer: GetSynchronizerUseCase
) : AndroidViewModel(application) {
val navigationCommand = exchangeRateRepository.navigationCommand
val backNavigationCommand = exchangeRateRepository.backNavigationCommand
val synchronizer = walletRepository.synchronizer
val walletRestoringState = walletRepository.walletRestoringState

View File

@ -4,11 +4,9 @@ package co.electriccoin.zcash.ui.screen.addressbook
import androidx.activity.compose.BackHandler
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.screen.addressbook.view.AddressBookView
import co.electriccoin.zcash.ui.screen.addressbook.viewmodel.AddressBookViewModel
@ -17,24 +15,11 @@ import org.koin.core.parameter.parametersOf
@Composable
internal fun WrapAddressBook(args: AddressBookArgs) {
val navController = LocalNavController.current
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val viewModel = koinViewModel<AddressBookViewModel> { parametersOf(args) }
val walletState by walletViewModel.walletStateInformation.collectAsStateWithLifecycle()
val state by viewModel.state.collectAsStateWithLifecycle()
LaunchedEffect(Unit) {
viewModel.navigationCommand.collect {
navController.navigate(it)
}
}
LaunchedEffect(Unit) {
viewModel.backNavigationCommand.collect {
navController.popBackStack()
}
}
BackHandler {
state.onBack()
}

View File

@ -3,6 +3,7 @@ package co.electriccoin.zcash.ui.screen.addressbook.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.AddressBookContact
import co.electriccoin.zcash.ui.common.usecase.ObserveAddressBookContactsUseCase
@ -16,7 +17,6 @@ import co.electriccoin.zcash.ui.screen.contact.AddContactArgs
import co.electriccoin.zcash.ui.screen.contact.UpdateContactArgs
import co.electriccoin.zcash.ui.screen.scan.ScanNavigationArgs
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.WhileSubscribed
import kotlinx.coroutines.flow.flowOn
@ -27,7 +27,8 @@ import kotlinx.coroutines.launch
class AddressBookViewModel(
observeAddressBookContacts: ObserveAddressBookContactsUseCase,
private val args: AddressBookArgs,
private val observeContactPicked: ObserveContactPickedUseCase
private val observeContactPicked: ObserveContactPickedUseCase,
private val navigationRouter: NavigationRouter,
) : ViewModel() {
val state =
observeAddressBookContacts()
@ -39,10 +40,6 @@ class AddressBookViewModel(
initialValue = createState(contacts = null)
)
val navigationCommand = MutableSharedFlow<String>()
val backNavigationCommand = MutableSharedFlow<Unit>()
private fun createState(contacts: List<AddressBookContact>?) =
AddressBookState(
isLoading = contacts == null,
@ -80,34 +77,25 @@ class AddressBookViewModel(
.joinToString(separator = "")
)
private fun onBack() =
viewModelScope.launch {
backNavigationCommand.emit(Unit)
}
private fun onBack() = navigationRouter.back()
private fun onContactClick(contact: AddressBookContact) =
viewModelScope.launch {
when (args) {
AddressBookArgs.DEFAULT -> {
navigationCommand.emit(UpdateContactArgs(contact.address))
navigationRouter.forward(UpdateContactArgs(contact.address))
}
AddressBookArgs.PICK_CONTACT -> {
observeContactPicked.onContactPicked(contact)
backNavigationCommand.emit(Unit)
navigationRouter.back()
}
}
}
private fun onAddContactManuallyClick() =
viewModelScope.launch {
navigationCommand.emit(AddContactArgs(null))
}
private fun onAddContactManuallyClick() = navigationRouter.forward(AddContactArgs(null))
private fun onScanContactClick() =
viewModelScope.launch {
navigationCommand.emit(ScanNavigationArgs(ScanNavigationArgs.ADDRESS_BOOK))
}
private fun onScanContactClick() = navigationRouter.forward(ScanNavigationArgs(ScanNavigationArgs.ADDRESS_BOOK))
}
private const val ADDRESS_MAX_LENGTH = 20

View File

@ -4,10 +4,8 @@ package co.electriccoin.zcash.ui.screen.advancedsettings
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
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.screen.advancedsettings.view.AdvancedSettings
import co.electriccoin.zcash.ui.screen.advancedsettings.viewmodel.AdvancedSettingsViewModel
@ -20,7 +18,6 @@ internal fun WrapAdvancedSettings(
goExportPrivateData: () -> Unit,
goSeedRecovery: () -> Unit,
) {
val navController = LocalNavController.current
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val viewModel = koinViewModel<AdvancedSettingsViewModel>()
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
@ -42,18 +39,6 @@ internal fun WrapAdvancedSettings(
viewModel.onBack()
}
LaunchedEffect(Unit) {
viewModel.navigationCommand.collect {
navController.navigate(it)
}
}
LaunchedEffect(Unit) {
viewModel.backNavigationCommand.collect {
navController.popBackStack()
}
}
AdvancedSettings(
state = state,
topAppBarSubTitleState = walletState,

View File

@ -3,6 +3,7 @@ package co.electriccoin.zcash.ui.screen.advancedsettings.viewmodel
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.NavigationTargets
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.usecase.SensitiveSettingsVisibleUseCase
@ -11,16 +12,15 @@ import co.electriccoin.zcash.ui.design.component.ZashiSettingsListItemState
import co.electriccoin.zcash.ui.design.util.stringRes
import co.electriccoin.zcash.ui.screen.advancedsettings.model.AdvancedSettingsState
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.WhileSubscribed
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
class AdvancedSettingsViewModel(
isSensitiveSettingsVisible: SensitiveSettingsVisibleUseCase
isSensitiveSettingsVisible: SensitiveSettingsVisibleUseCase,
private val navigationRouter: NavigationRouter,
) : ViewModel() {
val state: StateFlow<AdvancedSettingsState> =
isSensitiveSettingsVisible()
@ -68,21 +68,9 @@ class AdvancedSettingsViewModel(
),
)
val navigationCommand = MutableSharedFlow<String>()
val backNavigationCommand = MutableSharedFlow<Unit>()
private fun onChooseServerClick() = navigationRouter.forward(NavigationTargets.CHOOSE_SERVER)
private fun onChooseServerClick() =
viewModelScope.launch {
navigationCommand.emit(NavigationTargets.CHOOSE_SERVER)
}
private fun onCurrencyConversionClick() = navigationRouter.forward(NavigationTargets.SETTINGS_EXCHANGE_RATE_OPT_IN)
private fun onCurrencyConversionClick() =
viewModelScope.launch {
navigationCommand.emit(NavigationTargets.SETTINGS_EXCHANGE_RATE_OPT_IN)
}
fun onBack() =
viewModelScope.launch {
backNavigationCommand.emit(Unit)
}
fun onBack() = navigationRouter.back()
}

View File

@ -4,11 +4,9 @@ package co.electriccoin.zcash.ui.screen.contact
import androidx.activity.compose.BackHandler
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.screen.contact.view.ContactView
import co.electriccoin.zcash.ui.screen.contact.viewmodel.AddContactViewModel
@ -17,24 +15,11 @@ import org.koin.core.parameter.parametersOf
@Composable
internal fun WrapAddContact(address: String?) {
val navController = LocalNavController.current
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val viewModel = koinViewModel<AddContactViewModel> { parametersOf(address) }
val walletState by walletViewModel.walletStateInformation.collectAsStateWithLifecycle()
val state by viewModel.state.collectAsStateWithLifecycle()
LaunchedEffect(Unit) {
viewModel.navigationCommand.collect {
navController.navigate(it)
}
}
LaunchedEffect(Unit) {
viewModel.backNavigationCommand.collect {
navController.popBackStack()
}
}
BackHandler {
state?.onBack?.invoke()
}

View File

@ -4,11 +4,9 @@ package co.electriccoin.zcash.ui.screen.contact
import androidx.activity.compose.BackHandler
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.screen.contact.view.ContactView
import co.electriccoin.zcash.ui.screen.contact.viewmodel.UpdateContactViewModel
@ -17,24 +15,11 @@ import org.koin.core.parameter.parametersOf
@Composable
internal fun WrapUpdateContact(contactAddress: String) {
val navController = LocalNavController.current
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val viewModel = koinViewModel<UpdateContactViewModel> { parametersOf(contactAddress) }
val walletState by walletViewModel.walletStateInformation.collectAsStateWithLifecycle()
val state by viewModel.state.collectAsStateWithLifecycle()
LaunchedEffect(Unit) {
viewModel.navigationCommand.collect {
navController.navigate(it)
}
}
LaunchedEffect(Unit) {
viewModel.backNavigationCommand.collect {
navController.popBackStack()
}
}
BackHandler {
state?.onBack?.invoke()
}

View File

@ -3,6 +3,7 @@ package co.electriccoin.zcash.ui.screen.contact.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.usecase.SaveContactUseCase
import co.electriccoin.zcash.ui.common.usecase.ValidateContactAddressUseCase
@ -12,7 +13,6 @@ import co.electriccoin.zcash.ui.design.component.TextFieldState
import co.electriccoin.zcash.ui.design.util.stringRes
import co.electriccoin.zcash.ui.screen.contact.model.ContactState
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.WhileSubscribed
@ -26,7 +26,8 @@ class AddContactViewModel(
address: String? = null,
private val validateContactAddress: ValidateContactAddressUseCase,
private val validateContactName: ValidateContactNameUseCase,
private val saveContact: SaveContactUseCase
private val saveContact: SaveContactUseCase,
private val navigationRouter: NavigationRouter,
) : ViewModel() {
private val contactAddress = MutableStateFlow(address.orEmpty())
private val contactName = MutableStateFlow("")
@ -131,14 +132,7 @@ class AddContactViewModel(
initialValue = null
)
val navigationCommand = MutableSharedFlow<String>()
val backNavigationCommand = MutableSharedFlow<Unit>()
private fun onBack() =
viewModelScope.launch {
backNavigationCommand.emit(Unit)
}
private fun onBack() = navigationRouter.back()
private fun onSaveButtonClick() =
viewModelScope.launch {
@ -146,7 +140,7 @@ class AddContactViewModel(
isSavingContact.update { true }
saveContact(name = contactName.value, address = contactAddress.value)
backNavigationCommand.emit(Unit)
navigationRouter.back()
isSavingContact.update { false }
}
}

View File

@ -3,6 +3,7 @@ package co.electriccoin.zcash.ui.screen.contact.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.AddressBookContact
import co.electriccoin.zcash.ui.common.usecase.DeleteContactUseCase
@ -14,7 +15,6 @@ import co.electriccoin.zcash.ui.design.component.ButtonState
import co.electriccoin.zcash.ui.design.component.TextFieldState
import co.electriccoin.zcash.ui.design.util.stringRes
import co.electriccoin.zcash.ui.screen.contact.model.ContactState
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.WhileSubscribed
@ -30,7 +30,8 @@ class UpdateContactViewModel(
private val validateContactName: ValidateContactNameUseCase,
private val updateContact: UpdateContactUseCase,
private val deleteContact: DeleteContactUseCase,
private val getContactByAddress: GetContactByAddressUseCase
private val getContactByAddress: GetContactByAddressUseCase,
private val navigationRouter: NavigationRouter,
) : ViewModel() {
private var contact = MutableStateFlow<AddressBookContact?>(null)
private val contactAddress = MutableStateFlow("")
@ -148,10 +149,6 @@ class UpdateContactViewModel(
initialValue = null
)
val navigationCommand = MutableSharedFlow<String>()
val backNavigationCommand = MutableSharedFlow<Unit>()
init {
viewModelScope.launch {
getContactByAddress(originalContactAddress).let { contact ->
@ -163,10 +160,7 @@ class UpdateContactViewModel(
}
}
private fun onBack() =
viewModelScope.launch {
backNavigationCommand.emit(Unit)
}
private fun onBack() = navigationRouter.back()
private fun onUpdateButtonClick() =
viewModelScope.launch {
@ -174,7 +168,7 @@ class UpdateContactViewModel(
contact.value?.let {
isUpdatingContact.update { true }
updateContact(contact = it, name = contactName.value, address = contactAddress.value)
backNavigationCommand.emit(Unit)
navigationRouter.back()
isUpdatingContact.update { false }
}
}
@ -185,7 +179,7 @@ class UpdateContactViewModel(
contact.value?.let {
isDeletingContact.update { true }
deleteContact(it)
backNavigationCommand.emit(Unit)
navigationRouter.back()
isDeletingContact.update { false }
}
}

View File

@ -7,7 +7,6 @@ import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.di.koinActivityViewModel
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 co.electriccoin.zcash.ui.screen.about.util.WebBrowserUtil
import co.electriccoin.zcash.ui.screen.integrations.view.Integrations
@ -19,18 +18,11 @@ import org.koin.androidx.compose.koinViewModel
@Composable
internal fun WrapIntegrations() {
val activity = LocalActivity.current
val navController = LocalNavController.current
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val viewModel = koinViewModel<IntegrationsViewModel>()
val state by viewModel.state.collectAsStateWithLifecycle()
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
LaunchedEffect(Unit) {
viewModel.backNavigationCommand.collect {
navController.popBackStack()
}
}
LaunchedEffect(Unit) {
viewModel.coinbaseNavigationCommand.collect { uri ->
WebBrowserUtil.startActivity(activity, uri)

View File

@ -16,6 +16,7 @@ import cash.z.ecc.android.sdk.type.AddressType
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
import co.electriccoin.zcash.spackle.Twig
import co.electriccoin.zcash.ui.BuildConfig
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.common.provider.GetVersionInfoProvider
@ -57,9 +58,9 @@ class IntegrationsViewModel(
private val isCoinbaseAvailable: IsCoinbaseAvailableUseCase,
private val getSpendingKey: GetSpendingKeyUseCase,
private val context: Context,
private val biometricRepository: BiometricRepository
private val biometricRepository: BiometricRepository,
private val navigationRouter: NavigationRouter
) : ViewModel() {
val backNavigationCommand = MutableSharedFlow<Unit>()
val flexaNavigationCommand = MutableSharedFlow<Unit>()
val coinbaseNavigationCommand = MutableSharedFlow<String>()
@ -113,10 +114,7 @@ class IntegrationsViewModel(
initialValue = null
)
fun onBack() =
viewModelScope.launch {
backNavigationCommand.emit(Unit)
}
fun onBack() = navigationRouter.back()
private fun onBuyWithCoinbaseClicked() =
viewModelScope.launch {

View File

@ -10,7 +10,6 @@ import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.di.koinActivityViewModel
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.LocalNavController
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.screen.qrcode.model.QrCodeState
import co.electriccoin.zcash.ui.screen.qrcode.view.QrCodeView
@ -21,7 +20,6 @@ import org.koin.core.parameter.parametersOf
@Composable
internal fun WrapQrCode(addressType: Int) {
val context = LocalContext.current
val navController = LocalNavController.current
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val walletState by walletViewModel.walletStateInformation.collectAsStateWithLifecycle()
@ -31,11 +29,6 @@ internal fun WrapQrCode(addressType: Int) {
val snackbarHostState = remember { SnackbarHostState() }
LaunchedEffect(Unit) {
qrCodeViewModel.backNavigationCommand.collect {
navController.popBackStack()
}
}
LaunchedEffect(Unit) {
qrCodeViewModel.shareResultCommand.collect { sharedSuccessfully ->
if (!sharedSuccessfully) {

View File

@ -10,6 +10,7 @@ import androidx.lifecycle.viewModelScope
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
import co.electriccoin.zcash.spackle.Twig
import co.electriccoin.zcash.spackle.getInternalCacheDirSuspend
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.common.provider.GetVersionInfoProvider
@ -39,6 +40,7 @@ class QrCodeViewModel(
getAddresses: GetAddressesUseCase,
getVersionInfo: GetVersionInfoProvider,
private val copyToClipboard: CopyToClipboardUseCase,
private val navigationRouter: NavigationRouter,
) : ViewModel() {
private val versionInfo by lazy { getVersionInfo() }
@ -57,14 +59,9 @@ class QrCodeViewModel(
initialValue = QrCodeState.Loading
)
val backNavigationCommand = MutableSharedFlow<Unit>()
val shareResultCommand = MutableSharedFlow<Boolean>()
private fun onBack() =
viewModelScope.launch {
backNavigationCommand.emit(Unit)
}
private fun onBack() = navigationRouter.back()
private fun onQrCodeShareClick(
bitmap: ImageBitmap,

View File

@ -4,20 +4,16 @@ package co.electriccoin.zcash.ui.screen.receive
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
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.screen.receive.view.ReceiveView
import co.electriccoin.zcash.ui.screen.receive.viewmodel.ReceiveViewModel
@Composable
internal fun WrapReceive() {
val navController = LocalNavController.current
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
@ -26,12 +22,6 @@ internal fun WrapReceive() {
val snackbarHostState = remember { SnackbarHostState() }
LaunchedEffect(Unit) {
receiveViewModel.navigationCommand.collect {
navController.navigate(it)
}
}
ReceiveView(
state = receiveState,
topAppBarSubTitleState = walletState,

View File

@ -4,6 +4,7 @@ import android.app.Application
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.NavigationTargets
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.provider.GetVersionInfoProvider
@ -12,18 +13,17 @@ import co.electriccoin.zcash.ui.common.usecase.GetAddressesUseCase
import co.electriccoin.zcash.ui.screen.receive.model.ReceiveAddressType
import co.electriccoin.zcash.ui.screen.receive.model.ReceiveState
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.WhileSubscribed
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
class ReceiveViewModel(
private val application: Application,
getVersionInfo: GetVersionInfoProvider,
getAddresses: GetAddressesUseCase,
private val copyToClipboard: CopyToClipboardUseCase,
private val navigationRouter: NavigationRouter,
) : ViewModel() {
@OptIn(ExperimentalCoroutinesApi::class)
internal val state =
@ -47,20 +47,11 @@ class ReceiveViewModel(
initialValue = ReceiveState.Loading
)
val navigationCommand = MutableSharedFlow<String>()
private fun onRequestClick(addressType: ReceiveAddressType) =
viewModelScope.launch {
navigationCommand.emit("${NavigationTargets.REQUEST}/${addressType.ordinal}")
}
navigationRouter.forward("${NavigationTargets.REQUEST}/${addressType.ordinal}")
private fun onQrCodeClick(addressType: ReceiveAddressType) =
viewModelScope.launch {
navigationCommand.emit("${NavigationTargets.QR_CODE}/${addressType.ordinal}")
}
navigationRouter.forward("${NavigationTargets.QR_CODE}/${addressType.ordinal}")
private fun onSettingsClick() =
viewModelScope.launch {
navigationCommand.emit(NavigationTargets.SETTINGS)
}
private fun onSettingsClick() = navigationRouter.forward(NavigationTargets.SETTINGS)
}

View File

@ -12,7 +12,6 @@ import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.di.koinActivityViewModel
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.LocalNavController
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.screen.request.model.RequestState
import co.electriccoin.zcash.ui.screen.request.view.RequestView
@ -23,7 +22,6 @@ import org.koin.core.parameter.parametersOf
@Composable
internal fun WrapRequest(addressType: Int) {
val context = LocalContext.current
val navController = LocalNavController.current
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val walletState by walletViewModel.walletStateInformation.collectAsStateWithLifecycle()
@ -33,11 +31,6 @@ internal fun WrapRequest(addressType: Int) {
val snackbarHostState = remember { SnackbarHostState() }
LaunchedEffect(Unit) {
requestViewModel.backNavigationCommand.collect {
navController.popBackStack()
}
}
LaunchedEffect(Unit) {
requestViewModel.shareResultCommand.collect { sharedSuccessfully ->
if (!sharedSuccessfully) {

View File

@ -9,6 +9,7 @@ import cash.z.ecc.android.sdk.model.FiatCurrencyConversion
import cash.z.ecc.android.sdk.model.WalletAddress
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
import co.electriccoin.zcash.spackle.Twig
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.provider.GetMonetarySeparatorProvider
import co.electriccoin.zcash.ui.common.provider.GetZcashCurrencyProvider
@ -48,6 +49,7 @@ class RequestViewModel(
getMonetarySeparators: GetMonetarySeparatorProvider,
shareImageBitmap: ShareImageUseCase,
zip321BuildUriUseCase: Zip321BuildUriUseCase,
private val navigationRouter: NavigationRouter,
) : ViewModel() {
companion object {
private const val MAX_ZCASH_SUPPLY = 21_000_000
@ -132,8 +134,6 @@ class RequestViewModel(
initialValue = RequestState.Loading
)
val backNavigationCommand = MutableSharedFlow<Unit>()
val shareResultCommand = MutableSharedFlow<Boolean>()
private fun resolveExchangeRateValue(exchangeRateUsd: ExchangeRateState): FiatCurrencyConversion? {
@ -247,7 +247,7 @@ class RequestViewModel(
viewModelScope.launch {
when (stage.value) {
RequestStage.AMOUNT -> {
backNavigationCommand.emit(Unit)
navigationRouter.back()
}
RequestStage.MEMO -> {
stage.emit(RequestStage.AMOUNT)
@ -265,10 +265,7 @@ class RequestViewModel(
}
}
private fun onClose() =
viewModelScope.launch {
backNavigationCommand.emit(Unit)
}
private fun onClose() = navigationRouter.back()
private fun onAmountDone(conversion: FiatCurrencyConversion?) =
viewModelScope.launch {

View File

@ -2,11 +2,9 @@ package co.electriccoin.zcash.ui.screen.settings
import androidx.activity.compose.BackHandler
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.screen.settings.view.Settings
import co.electriccoin.zcash.ui.screen.settings.viewmodel.SettingsViewModel
@ -14,24 +12,11 @@ import org.koin.androidx.compose.koinViewModel
@Composable
internal fun WrapSettings() {
val navController = LocalNavController.current
val walletViewModel = koinActivityViewModel<WalletViewModel>()
val settingsViewModel = koinViewModel<SettingsViewModel>()
val state by settingsViewModel.state.collectAsStateWithLifecycle()
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
LaunchedEffect(Unit) {
settingsViewModel.navigationCommand.collect {
navController.navigate(it)
}
}
LaunchedEffect(Unit) {
settingsViewModel.backNavigationCommand.collect {
navController.popBackStack()
}
}
BackHandler {
settingsViewModel.onBack()
}

View File

@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
import co.electriccoin.zcash.preference.StandardPreferenceProvider
import co.electriccoin.zcash.preference.model.entry.BooleanPreferenceDefault
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.NavigationTargets.ABOUT
import co.electriccoin.zcash.ui.NavigationTargets.ADVANCED_SETTINGS
import co.electriccoin.zcash.ui.NavigationTargets.INTEGRATIONS
@ -25,7 +26,6 @@ import co.electriccoin.zcash.ui.screen.settings.model.SettingsState
import co.electriccoin.zcash.ui.screen.settings.model.SettingsTroubleshootingState
import co.electriccoin.zcash.ui.screen.settings.model.TroubleshootingItemState
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.WhileSubscribed
@ -43,6 +43,7 @@ class SettingsViewModel(
private val standardPreferenceProvider: StandardPreferenceProvider,
private val getVersionInfo: GetVersionInfoProvider,
private val rescanBlockchain: RescanBlockchainUseCase,
private val navigationRouter: NavigationRouter,
) : ViewModel() {
private val versionInfo by lazy { getVersionInfo() }
@ -160,9 +161,6 @@ class SettingsViewModel(
version = stringRes(R.string.settings_version, versionInfo.versionName)
)
val navigationCommand = MutableSharedFlow<String>()
val backNavigationCommand = MutableSharedFlow<Unit>()
private fun setAnalyticsEnabled(enabled: Boolean) {
setBooleanPreference(StandardPreferenceKeys.IS_ANALYTICS_ENABLED, enabled)
}
@ -189,42 +187,19 @@ class SettingsViewModel(
}
}
fun onBack() =
viewModelScope.launch {
backNavigationCommand.emit(Unit)
}
fun onBack() = navigationRouter.back()
private fun onIntegrationsClick() =
viewModelScope.launch {
navigationCommand.emit(INTEGRATIONS)
}
private fun onIntegrationsClick() = navigationRouter.forward(INTEGRATIONS)
private fun onAdvancedSettingsClick() =
viewModelScope.launch {
navigationCommand.emit(ADVANCED_SETTINGS)
}
private fun onAdvancedSettingsClick() = navigationRouter.forward(ADVANCED_SETTINGS)
private fun onAboutUsClick() =
viewModelScope.launch {
navigationCommand.emit(ABOUT)
}
private fun onAboutUsClick() = navigationRouter.forward(ABOUT)
private fun onSendUsFeedbackClick() =
viewModelScope.launch {
navigationCommand.emit(SUPPORT)
}
private fun onSendUsFeedbackClick() = navigationRouter.forward(SUPPORT)
private fun onAddressBookClick() {
viewModelScope.launch {
navigationCommand.emit(AddressBookArgs(AddressBookArgs.DEFAULT))
}
}
private fun onAddressBookClick() = navigationRouter.forward(AddressBookArgs(AddressBookArgs.DEFAULT))
private fun onWhatsNewClick() {
viewModelScope.launch {
navigationCommand.emit(WHATS_NEW)
}
}
private fun onWhatsNewClick() = navigationRouter.forward(WHATS_NEW)
private fun booleanStateFlow(default: BooleanPreferenceDefault): StateFlow<Boolean?> =
flow<Boolean?> {