Updated on 2026-08-14

This commit is contained in:
Tangem 2025-04-10 14:56:55 +03:00
commit 0e9681442a
19 changed files with 126 additions and 125 deletions

View file

@ -1,19 +1,18 @@
package com.tangem.common.test.utils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@OptIn(ExperimentalCoroutinesApi::class)
fun <T> CoroutineScope.getEmittedValues(testScheduler: TestCoroutineScheduler, actual: Flow<T>): List<T> {
fun <T> TestScope.getEmittedValues(flow: Flow<T>): List<T> {
val values = mutableListOf<T>()
launch(UnconfinedTestDispatcher(testScheduler)) {
actual.toList(values)
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
flow.toList(values)
}
return values

View file

@ -65,7 +65,7 @@ internal class DefaultMultiNetworkStatusProducerTest {
// check after producer.produce()
verify { networksStatusesStore.get(params.userWalletId) }
val values = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values = getEmittedValues(flow = actual)
// Check after flow was observed by subscriber (getEmittedValues).
// Otherwise, userWalletsStore.getSyncOrNull is not called.
@ -95,7 +95,7 @@ internal class DefaultMultiNetworkStatusProducerTest {
networksStatusesFlow.emit(statuses.map(NetworkStatus::toSimple).toSet())
val values1 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values1 = getEmittedValues(flow = actual)
// Check after flow was observed by subscriber (getEmittedValues).
// Otherwise, userWalletsStore.getSyncOrNull is not called.
@ -112,7 +112,7 @@ internal class DefaultMultiNetworkStatusProducerTest {
networksStatusesFlow.emit(updatedStatuses.map(NetworkStatus::toSimple).toSet())
val values2 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values2 = getEmittedValues(flow = actual)
// Check after flow was observed by subscriber (getEmittedValues).
// Otherwise, userWalletsStore.getSyncOrNull is not called.
@ -143,7 +143,7 @@ internal class DefaultMultiNetworkStatusProducerTest {
networksStatusesFlow.emit(statuses.map(NetworkStatus::toSimple).toSet())
val values1 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values1 = getEmittedValues(flow = actual)
// Check after flow was observed by subscriber (getEmittedValues).
// Otherwise, userWalletsStore.getSyncOrNull is not called.
@ -155,7 +155,7 @@ internal class DefaultMultiNetworkStatusProducerTest {
// second emit
networksStatusesFlow.emit(statuses.map(NetworkStatus::toSimple).toSet())
val values2 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values2 = getEmittedValues(flow = actual)
// Check after flow was observed by subscriber (getEmittedValues).
// Otherwise, userWalletsStore.getSyncOrNull is not called.
@ -191,7 +191,7 @@ internal class DefaultMultiNetworkStatusProducerTest {
// check after producer.produce()
verify { networksStatusesStore.get(params.userWalletId) }
val values1 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values1 = getEmittedValues(flow = actual)
// Check after flow was observed by subscriber (getEmittedValues).
// Otherwise, userWalletsStore.getSyncOrNull is not called.
@ -202,7 +202,7 @@ internal class DefaultMultiNetworkStatusProducerTest {
innerFlow.emit(value = true)
val values2 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values2 = getEmittedValues(flow = actual)
// Check after flow was observed by subscriber (getEmittedValues).
// Otherwise, userWalletsStore.getSyncOrNull is not called.
@ -222,7 +222,7 @@ internal class DefaultMultiNetworkStatusProducerTest {
// check after producer.produce()
verify { networksStatusesStore.get(params.userWalletId) }
val values = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values = getEmittedValues(flow = actual)
verify(inverse = true) { userWalletsStore.getSyncOrNull(params.userWalletId) }
@ -248,7 +248,7 @@ internal class DefaultMultiNetworkStatusProducerTest {
// check after producer.produce()
verify { networksStatusesStore.get(params.userWalletId) }
val values = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values = getEmittedValues(flow = actual)
// Check after flow was observed by subscriber (getEmittedValues).
// Otherwise, userWalletsStore.getSyncOrNull is not called.

View file

@ -53,7 +53,7 @@ internal class DefaultSingleNetworkStatusProducerTest {
verify { multiNetworkStatusSupplier(multiParams) }
val values = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values = getEmittedValues(flow = actual)
Truth.assertThat(values.size).isEqualTo(1)
Truth.assertThat(values).isEqualTo(listOf(status))
@ -74,7 +74,7 @@ internal class DefaultSingleNetworkStatusProducerTest {
val status = MockNetworkStatusFactory.createMissedDerivation(params.network)
expected.emit(value = setOf(status))
val values1 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values1 = getEmittedValues(flow = actual)
Truth.assertThat(values1.size).isEqualTo(1)
Truth.assertThat(values1).isEqualTo(listOf(status))
@ -83,7 +83,7 @@ internal class DefaultSingleNetworkStatusProducerTest {
val updatedStatus = status.copy(value = NetworkStatus.Unreachable(null))
expected.emit(value = setOf(updatedStatus))
val values2 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values2 = getEmittedValues(flow = actual)
Truth.assertThat(values2.size).isEqualTo(2)
Truth.assertThat(values2).isEqualTo(listOf(status, updatedStatus))
@ -104,7 +104,7 @@ internal class DefaultSingleNetworkStatusProducerTest {
val status = MockNetworkStatusFactory.createMissedDerivation(params.network)
expected.emit(value = setOf(status))
val values1 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values1 = getEmittedValues(flow = actual)
Truth.assertThat(values1.size).isEqualTo(1)
Truth.assertThat(values1).isEqualTo(listOf(status))
@ -112,7 +112,7 @@ internal class DefaultSingleNetworkStatusProducerTest {
// second emit
expected.emit(value = setOf(status))
val values2 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values2 = getEmittedValues(flow = actual)
Truth.assertThat(values2.size).isEqualTo(1)
Truth.assertThat(values2).isEqualTo(listOf(status))
@ -140,7 +140,7 @@ internal class DefaultSingleNetworkStatusProducerTest {
verify { multiNetworkStatusSupplier(multiParams) }
val values1 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values1 = getEmittedValues(flow = actual)
Truth.assertThat(values1.size).isEqualTo(1)
val fallbackStatus = MockNetworkStatusFactory.createUnreachable(params.network)
@ -148,7 +148,7 @@ internal class DefaultSingleNetworkStatusProducerTest {
innerFlow.emit(value = true)
val values2 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values2 = getEmittedValues(flow = actual)
Truth.assertThat(values2.size).isEqualTo(1)
Truth.assertThat(values2).isEqualTo(listOf(status))
}
@ -166,7 +166,7 @@ internal class DefaultSingleNetworkStatusProducerTest {
verify { multiNetworkStatusSupplier(multiParams) }
val values = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values = getEmittedValues(flow = actual)
Truth.assertThat(values.size).isEqualTo(0)
}

View file

@ -30,7 +30,7 @@ internal class NetworksStatusesStoreGetMethodTest {
fun `test get if runtime store is empty`() = runTest {
val actual = store.get(userWalletId = userWalletId)
val values = backgroundScope.getEmittedValues(testScheduler, actual)
val values = getEmittedValues(flow = actual)
Truth.assertThat(values).isEqualTo(emptyList<Set<SimpleNetworkStatus>>())
}
@ -41,7 +41,7 @@ internal class NetworksStatusesStoreGetMethodTest {
val actual = store.get(userWalletId = userWalletId)
val values = backgroundScope.getEmittedValues(testScheduler, actual)
val values = getEmittedValues(flow = actual)
Truth.assertThat(values).isEqualTo(emptyList<Set<SimpleNetworkStatus>>())
}
@ -54,7 +54,7 @@ internal class NetworksStatusesStoreGetMethodTest {
val actual = store.get(userWalletId = userWalletId)
val values = backgroundScope.getEmittedValues(testScheduler, actual)
val values = getEmittedValues(flow = actual)
Truth.assertThat(values.size).isEqualTo(1)
Truth.assertThat(values).isEqualTo(listOf(emptySet<SimpleNetworkStatus>()))
@ -70,7 +70,7 @@ internal class NetworksStatusesStoreGetMethodTest {
val actual = store.get(userWalletId = userWalletId)
val values = backgroundScope.getEmittedValues(testScheduler, actual)
val values = getEmittedValues(flow = actual)
Truth.assertThat(values.size).isEqualTo(1)
Truth.assertThat(values).isEqualTo(listOf(setOf(status.toSimple())))

View file

@ -49,7 +49,7 @@ internal class DefaultSingleQuoteProducerTest {
verify { quotesStore.get() }
val values = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values = getEmittedValues(flow = actual)
Truth.assertThat(values.size).isEqualTo(1)
Truth.assertThat(values).isEqualTo(listOf(status))
@ -69,7 +69,7 @@ internal class DefaultSingleQuoteProducerTest {
val status = Quote.Empty(rawCurrencyId = params.rawCurrencyId)
storeQuote.emit(value = setOf(status))
val values1 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values1 = getEmittedValues(flow = actual)
Truth.assertThat(values1.size).isEqualTo(1)
Truth.assertThat(values1).isEqualTo(listOf(status))
@ -83,7 +83,7 @@ internal class DefaultSingleQuoteProducerTest {
)
storeQuote.emit(value = setOf(updatedStatus))
val values2 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values2 = getEmittedValues(flow = actual)
Truth.assertThat(values2.size).isEqualTo(2)
Truth.assertThat(values2).isEqualTo(listOf(status, updatedStatus))
@ -103,7 +103,7 @@ internal class DefaultSingleQuoteProducerTest {
val status = Quote.Empty(rawCurrencyId = params.rawCurrencyId)
storeQuote.emit(value = setOf(status))
val values1 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values1 = getEmittedValues(flow = actual)
Truth.assertThat(values1.size).isEqualTo(1)
Truth.assertThat(values1).isEqualTo(listOf(status))
@ -111,7 +111,7 @@ internal class DefaultSingleQuoteProducerTest {
// second emit
storeQuote.emit(value = setOf(status))
val values2 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values2 = getEmittedValues(flow = actual)
Truth.assertThat(values2.size).isEqualTo(1)
Truth.assertThat(values2).isEqualTo(listOf(status))
@ -143,7 +143,7 @@ internal class DefaultSingleQuoteProducerTest {
verify { quotesStore.get() }
val values1 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values1 = getEmittedValues(flow = actual)
Truth.assertThat(values1.size).isEqualTo(1)
val fallbackStatus = Quote.Empty(rawCurrencyId = params.rawCurrencyId)
@ -151,7 +151,7 @@ internal class DefaultSingleQuoteProducerTest {
innerFlow.emit(value = true)
val values2 = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values2 = getEmittedValues(flow = actual)
Truth.assertThat(values2.size).isEqualTo(1)
Truth.assertThat(values2).isEqualTo(listOf(status))
}
@ -170,7 +170,7 @@ internal class DefaultSingleQuoteProducerTest {
verify { quotesStore.get() }
val values = backgroundScope.getEmittedValues(testScheduler = testScheduler, actual = actual)
val values = getEmittedValues(flow = actual)
Truth.assertThat(values.size).isEqualTo(0)
}

View file

@ -30,7 +30,7 @@ internal class QuotesStoreGetMethodTest {
fun `test get if runtime store is empty`() = runTest {
val actual = store.get()
val values = backgroundScope.getEmittedValues(testScheduler, actual)
val values = getEmittedValues(flow = actual)
Truth.assertThat(values).isEqualTo(emptyList<Set<Quote>>())
}
@ -41,7 +41,7 @@ internal class QuotesStoreGetMethodTest {
val actual = store.get()
val values = backgroundScope.getEmittedValues(testScheduler, actual)
val values = getEmittedValues(flow = actual)
Truth.assertThat(values).isEqualTo(listOf(emptySet<Quote>()))
}
@ -55,7 +55,7 @@ internal class QuotesStoreGetMethodTest {
val actual = store.get()
val values = backgroundScope.getEmittedValues(testScheduler, actual)
val values = getEmittedValues(flow = actual)
Truth.assertThat(values.size).isEqualTo(1)
Truth.assertThat(values).isEqualTo(listOf(setOf(btcQuote.toDomain(), ethQuote.toDomain())))

View file

@ -1,35 +1,34 @@
package com.tangem.features.send.v2.send
package com.tangem.features.send.v2.common
import com.tangem.core.decompose.navigation.Route
import kotlinx.serialization.Serializable
@Serializable
internal sealed class SendRoute : Route {
internal sealed class CommonSendRoute : Route {
abstract val isEditMode: Boolean
@Serializable
data object Empty : SendRoute() {
data object Empty : CommonSendRoute() {
override val isEditMode = false
}
@Serializable
data object Confirm : SendRoute() {
data object Confirm : CommonSendRoute() {
override val isEditMode: Boolean = false
}
@Serializable
data class Destination(
override val isEditMode: Boolean,
) : SendRoute()
) : CommonSendRoute()
@Serializable
data class Amount(
override val isEditMode: Boolean,
) : SendRoute()
) : CommonSendRoute()
@Serializable
data class Fee(
override val isEditMode: Boolean = true,
) : SendRoute()
data object Fee : CommonSendRoute() {
override val isEditMode: Boolean = true
}
}

View file

@ -13,11 +13,14 @@ import com.tangem.core.ui.components.appbar.AppBarWithBackButtonAndIcon
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.send.v2.send.SendRoute
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.ui.state.NavigationUM
@Composable
internal fun SendContent(navigationUM: NavigationUM, stackState: ChildStack<SendRoute, ComposableContentComponent>) {
internal fun SendContent(
navigationUM: NavigationUM,
stackState: ChildStack<CommonSendRoute, ComposableContentComponent>,
) {
Column(
modifier = Modifier.Companion
.background(color = TangemTheme.colors.background.tertiary)

View file

@ -19,6 +19,7 @@ import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.decompose.navigation.inner.InnerRouter
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.features.send.v2.api.SendComponent
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.PredefinedValues
import com.tangem.features.send.v2.common.ui.SendContent
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
@ -45,17 +46,17 @@ internal class DefaultSendComponent @AssistedInject constructor(
private val analyticsEventHandler: AnalyticsEventHandler,
) : SendComponent, AppComponentContext by appComponentContext {
private val stackNavigation = StackNavigation<SendRoute>()
private val stackNavigation = StackNavigation<CommonSendRoute>()
private val innerRouter = InnerRouter<SendRoute>(
private val innerRouter = InnerRouter<CommonSendRoute>(
stackNavigation = stackNavigation,
popCallback = { onChildBack() },
)
private val initialRoute = if (params.amount == null) {
SendRoute.Destination(isEditMode = false)
CommonSendRoute.Destination(isEditMode = false)
} else {
SendRoute.Empty
CommonSendRoute.Empty
}
private val currentRoute = MutableStateFlow(initialRoute)
@ -118,42 +119,56 @@ internal class DefaultSendComponent @AssistedInject constructor(
)
}
private fun createChild(route: SendRoute, factoryContext: AppComponentContext) = when (route) {
SendRoute.Empty -> getStubComponent()
is SendRoute.Destination -> getDestinationComponent(factoryContext, route)
is SendRoute.Amount -> getAmountComponent(factoryContext, route)
is SendRoute.Fee -> getFeeComponent(factoryContext)
SendRoute.Confirm -> getConfirmComponent(factoryContext)
private fun createChild(route: CommonSendRoute, factoryContext: AppComponentContext) = when (route) {
CommonSendRoute.Empty -> getStubComponent()
is CommonSendRoute.Destination -> getDestinationComponent(factoryContext, route)
is CommonSendRoute.Amount -> getAmountComponent(factoryContext, route)
is CommonSendRoute.Fee -> getFeeComponent(factoryContext)
CommonSendRoute.Confirm -> getConfirmComponent(factoryContext)
}
private fun getDestinationComponent(factoryContext: AppComponentContext, route: SendRoute) =
private fun getDestinationComponent(factoryContext: AppComponentContext, route: CommonSendRoute) =
SendDestinationComponent(
appComponentContext = factoryContext,
params = SendDestinationComponentParams.DestinationParams(
state = model.uiState.value.destinationUM,
currentRoute = currentRoute.filterIsInstance<SendRoute.Destination>(),
currentRoute = currentRoute.filterIsInstance<CommonSendRoute.Destination>(),
isBalanceHidingFlow = model.isBalanceHiddenFlow,
analyticsCategoryName = SendAnalyticEvents.SEND_CATEGORY,
userWalletId = params.userWalletId,
cryptoCurrency = params.currency,
callback = model,
isEditMode = route.isEditMode,
onBackClick = ::onChildBack,
onNextClick = {
if (route.isEditMode) {
onChildBack()
} else {
innerRouter.push(CommonSendRoute.Amount(isEditMode = false))
}
},
),
)
private fun getAmountComponent(factoryContext: AppComponentContext, route: SendRoute) = SendAmountComponent(
private fun getAmountComponent(factoryContext: AppComponentContext, route: CommonSendRoute) = SendAmountComponent(
appComponentContext = factoryContext,
params = SendAmountComponentParams.AmountParams(
state = model.uiState.value.amountUM,
currentRoute = currentRoute.filterIsInstance<SendRoute.Amount>(),
currentRoute = currentRoute.filterIsInstance<CommonSendRoute.Amount>(),
isBalanceHidingFlow = model.isBalanceHiddenFlow,
analyticsCategoryName = SendAnalyticEvents.SEND_CATEGORY,
userWallet = model.userWallet,
appCurrency = model.appCurrency,
cryptoCurrencyStatus = model.cryptoCurrencyStatus,
callback = model,
isEditMode = route.isEditMode,
predefinedValues = model.predefinedValues,
onBackClick = ::onChildBack,
onNextClick = {
if (route.isEditMode) {
onChildBack()
} else {
innerRouter.push(CommonSendRoute.Confirm)
}
},
),
)
@ -166,7 +181,7 @@ internal class DefaultSendComponent @AssistedInject constructor(
appComponentContext = factoryContext,
params = SendFeeComponentParams.FeeParams(
state = model.uiState.value.feeUM,
currentRoute = currentRoute.filterIsInstance<SendRoute.Fee>(),
currentRoute = currentRoute.filterIsInstance<CommonSendRoute.Fee>(),
analyticsCategoryName = SendAnalyticEvents.SEND_CATEGORY,
userWallet = model.userWallet,
cryptoCurrencyStatus = model.cryptoCurrencyStatus,
@ -175,6 +190,7 @@ internal class DefaultSendComponent @AssistedInject constructor(
sendAmount = sendAmount,
destinationAddress = destinationAddress,
callback = model,
onNextClick = ::onChildBack,
),
)
} else {
@ -202,7 +218,7 @@ internal class DefaultSendComponent @AssistedInject constructor(
params = SendConfirmComponent.Params(
state = model.uiState.value,
userWallet = model.userWallet,
currentRoute = currentRoute.filterIsInstance<SendRoute.Confirm>(),
currentRoute = currentRoute.filterIsInstance<CommonSendRoute.Confirm>(),
isBalanceHidingFlow = model.isBalanceHiddenFlow,
analyticsCategoryName = SendAnalyticEvents.SEND_CATEGORY,
cryptoCurrencyStatus = model.cryptoCurrencyStatus,
@ -217,7 +233,7 @@ internal class DefaultSendComponent @AssistedInject constructor(
private fun getStubComponent() = ComposableContentComponent { }
private fun onChildBack() {
val isEmptyRoute = childStack.value.active.configuration == SendRoute.Empty
val isEmptyRoute = childStack.value.active.configuration == CommonSendRoute.Empty
val isEmptyStack = childStack.value.backStack.isEmpty()
val isSuccess = model.uiState.value.confirmUM is ConfirmUM.Success

View file

@ -11,8 +11,8 @@ import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.PredefinedValues
import com.tangem.features.send.v2.send.SendRoute
import com.tangem.features.send.v2.send.confirm.model.SendConfirmModel
import com.tangem.features.send.v2.send.confirm.ui.SendConfirmContent
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
@ -141,7 +141,7 @@ internal class SendConfirmComponent(
val feeCryptoCurrencyStatus: CryptoCurrencyStatus,
val appCurrency: AppCurrency,
val callback: ModelCallback,
val currentRoute: Flow<SendRoute.Confirm>,
val currentRoute: Flow<CommonSendRoute.Confirm>,
val isBalanceHidingFlow: StateFlow<Boolean>,
val predefinedValues: PredefinedValues,
)

View file

@ -30,11 +30,11 @@ import com.tangem.domain.transaction.usecase.CreateTransactionUseCase
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.SendBalanceUpdater
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
import com.tangem.features.send.v2.common.ui.state.NavigationUM
import com.tangem.features.send.v2.impl.R
import com.tangem.features.send.v2.send.SendRoute
import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents
import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents.SendScreenSource
import com.tangem.features.send.v2.send.analytics.SendAnalyticHelper
@ -164,7 +164,7 @@ internal class SendConfirmModel @Inject constructor(
it.copy(confirmUM = confirmUM?.copy(showTapHelp = false) ?: it.confirmUM)
}
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Address))
router.push(SendRoute.Destination(isEditMode = true))
router.push(CommonSendRoute.Destination(isEditMode = true))
}
}
@ -176,7 +176,7 @@ internal class SendConfirmModel @Inject constructor(
it.copy(confirmUM = confirmUM?.copy(showTapHelp = false) ?: it.confirmUM)
}
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Amount))
router.push(SendRoute.Amount(isEditMode = true))
router.push(CommonSendRoute.Amount(isEditMode = true))
}
}
@ -188,7 +188,7 @@ internal class SendConfirmModel @Inject constructor(
it.copy(confirmUM = confirmUM?.copy(showTapHelp = false) ?: it.confirmUM)
}
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Fee))
router.push(SendRoute.Fee())
router.push(CommonSendRoute.Fee)
}
}

View file

@ -12,7 +12,7 @@ import dagger.multibindings.IntoMap
@Module
@InstallIn(ModelComponent::class)
internal interface SendModelModule {
internal interface CommonSendModelModule {
@Binds
@IntoMap

View file

@ -29,10 +29,10 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.send.v2.api.SendComponent
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.PredefinedValues
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
import com.tangem.features.send.v2.common.ui.state.NavigationUM
import com.tangem.features.send.v2.send.SendRoute
import com.tangem.features.send.v2.send.confirm.SendConfirmComponent
import com.tangem.features.send.v2.send.confirm.model.SendConfirmAlertFactory
import com.tangem.features.send.v2.send.ui.state.SendUM
@ -215,7 +215,7 @@ internal class SendModel @Inject constructor(
feeCryptoCurrencyStatus = feeCurrencyStatus
if (params.amount != null) {
router.replaceAll(SendRoute.Confirm)
router.replaceAll(CommonSendRoute.Confirm)
}
}

View file

@ -4,8 +4,8 @@ import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.PredefinedValues
import com.tangem.features.send.v2.send.SendRoute
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponent.ModelCallback
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@ -26,10 +26,11 @@ internal sealed class SendAmountComponentParams {
override val appCurrency: AppCurrency,
override val cryptoCurrencyStatus: CryptoCurrencyStatus,
override val predefinedValues: PredefinedValues,
val isEditMode: Boolean,
val callback: ModelCallback,
val currentRoute: Flow<SendRoute.Amount>,
val currentRoute: Flow<CommonSendRoute.Amount>,
val isBalanceHidingFlow: StateFlow<Boolean>,
val onBackClick: () -> Unit,
val onNextClick: () -> Unit,
) : SendAmountComponentParams()
data class AmountBlockParams(

View file

@ -12,7 +12,6 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
@ -21,7 +20,6 @@ import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase
import com.tangem.features.send.v2.common.PredefinedValues
import com.tangem.features.send.v2.common.ui.state.NavigationUM
import com.tangem.features.send.v2.impl.R
import com.tangem.features.send.v2.send.SendRoute
import com.tangem.features.send.v2.send.ui.state.ButtonsUM
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponentParams
import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceListener
@ -41,7 +39,6 @@ import javax.inject.Inject
internal class SendAmountModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase,
private val sendAmountReduceListener: SendAmountReduceListener,
private val analyticsEventHandler: AnalyticsEventHandler,
@ -161,11 +158,6 @@ internal class SendAmountModel @Inject constructor(
)
}
saveResult()
if ((params as? SendAmountComponentParams.AmountParams)?.isEditMode == true) {
router.pop()
} else {
router.push(SendRoute.Confirm)
}
}
private fun subscribeOnAmountReduceToTriggerUpdates() {
@ -223,7 +215,7 @@ internal class SendAmountModel @Inject constructor(
if (!route.isEditMode) {
saveResult()
}
router.pop()
params.onBackClick()
},
primaryButton = ButtonsUM.PrimaryButtonUM(
text = if (route.isEditMode) {
@ -232,7 +224,10 @@ internal class SendAmountModel @Inject constructor(
resourceReference(R.string.common_next)
},
isEnabled = state.isPrimaryButtonEnabled,
onClick = ::onAmountNext,
onClick = {
onAmountNext()
params.onNextClick()
},
),
prevButton = ButtonsUM.PrimaryButtonUM(
text = TextReference.EMPTY,
@ -240,7 +235,7 @@ internal class SendAmountModel @Inject constructor(
isEnabled = true,
onClick = {
saveResult()
router.pop()
params.onBackClick()
},
).takeIf { route.isEditMode.not() },
secondaryPairButtonsUM = null,

View file

@ -2,8 +2,8 @@ package com.tangem.features.send.v2.subcomponents.destination
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.PredefinedValues
import com.tangem.features.send.v2.send.SendRoute
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationComponent.ModelCallback
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM
import kotlinx.coroutines.flow.Flow
@ -22,9 +22,10 @@ internal sealed class SendDestinationComponentParams {
override val cryptoCurrency: CryptoCurrency,
override val userWalletId: UserWalletId,
val isBalanceHidingFlow: StateFlow<Boolean>,
val currentRoute: Flow<SendRoute.Destination>,
val currentRoute: Flow<CommonSendRoute.Destination>,
val callback: ModelCallback,
val isEditMode: Boolean,
val onBackClick: () -> Unit,
val onNextClick: () -> Unit,
) : SendDestinationComponentParams()
data class DestinationBlockParams(

View file

@ -3,7 +3,6 @@ package com.tangem.features.send.v2.subcomponents.destination.model
import androidx.compose.runtime.Stable
import arrow.core.getOrElse
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
@ -25,7 +24,6 @@ import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.send.v2.common.PredefinedValues
import com.tangem.features.send.v2.common.ui.state.NavigationUM
import com.tangem.features.send.v2.impl.R
import com.tangem.features.send.v2.send.SendRoute
import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents
import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents.SendScreenSource
import com.tangem.features.send.v2.send.ui.state.ButtonsUM
@ -55,7 +53,6 @@ internal class SendDestinationModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
private val appRouter: AppRouter,
private val validateWalletAddressUseCase: ValidateWalletAddressUseCase,
private val validateWalletMemoUseCase: ValidateWalletMemoUseCase,
private val getWalletsUseCase: GetWalletsUseCase,
@ -272,7 +269,8 @@ internal class SendDestinationModel @Inject constructor(
private fun autoNextFromRecipient(type: EnterAddressSource?, isValidAddress: Boolean, isValidMemo: Boolean) {
val isRecent = type == EnterAddressSource.RecentAddress
if (isRecent && isValidAddress && isValidMemo) onNextClick()
if (isRecent && isValidAddress && isValidMemo) saveResult()
(params as? SendDestinationComponentParams.DestinationParams)?.onNextClick?.invoke()
}
private fun saveResult() {
@ -280,15 +278,6 @@ internal class SendDestinationModel @Inject constructor(
params.callback.onDestinationResult(uiState.value)
}
private fun onNextClick() {
saveResult()
if ((params as? SendDestinationComponentParams.DestinationParams)?.isEditMode == true) {
router.pop()
} else {
router.push(SendRoute.Amount(isEditMode = false))
}
}
private fun configDestinationNavigation() {
val params = params as? SendDestinationComponentParams.DestinationParams ?: return
combine(
@ -306,9 +295,7 @@ internal class SendDestinationModel @Inject constructor(
R.drawable.ic_close_24
},
backIconClick = {
if (route.isEditMode) {
router.pop()
} else {
if (!route.isEditMode) {
analyticsEventHandler.send(
SendAnalyticEvents.CloseButtonClicked(
source = SendScreenSource.Address,
@ -316,8 +303,8 @@ internal class SendDestinationModel @Inject constructor(
isValid = state.isPrimaryButtonEnabled,
),
)
appRouter.pop()
}
params.onBackClick()
},
additionalIconRes = R.drawable.ic_qrcode_scan_24,
additionalIconClick = ::onQrCodeScanClick,
@ -328,7 +315,10 @@ internal class SendDestinationModel @Inject constructor(
resourceReference(R.string.common_next)
},
isEnabled = state.isPrimaryButtonEnabled,
onClick = ::onNextClick,
onClick = {
saveResult()
params.onNextClick()
},
),
prevButton = null,
secondaryPairButtonsUM = null,

View file

@ -3,7 +3,7 @@ package com.tangem.features.send.v2.subcomponents.fee
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.send.v2.send.SendRoute
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@ -29,8 +29,9 @@ internal sealed class SendFeeComponentParams {
override val appCurrency: AppCurrency,
override val sendAmount: BigDecimal,
override val destinationAddress: String,
val currentRoute: Flow<SendRoute.Fee>,
val currentRoute: Flow<CommonSendRoute.Fee>,
val callback: SendFeeComponent.ModelCallback,
val onNextClick: () -> Unit,
) : SendFeeComponentParams()
data class FeeBlockParams(

View file

@ -6,7 +6,6 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.transaction.usecase.GetFeeUseCase
@ -38,7 +37,6 @@ import javax.inject.Inject
internal class SendFeeModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
private val isFeeApproximateUseCase: IsFeeApproximateUseCase,
private val getFeeUseCase: GetFeeUseCase,
private val urlOpener: UrlOpener,
@ -132,10 +130,10 @@ internal class SendFeeModel @Inject constructor(
),
)
navigate()
saveResult()
}
} else {
navigate()
saveResult()
}
}
@ -267,11 +265,6 @@ internal class SendFeeModel @Inject constructor(
)
}
private fun navigate() {
saveResult()
router.pop()
}
private fun configFeeNavigation() {
val params = params as? SendFeeComponentParams.FeeParams ?: return
combine(
@ -284,11 +277,14 @@ internal class SendFeeModel @Inject constructor(
title = resourceReference(R.string.common_fee_selector_title),
subtitle = null,
backIconRes = R.drawable.ic_back_24,
backIconClick = router::pop,
backIconClick = params.onNextClick,
primaryButton = ButtonsUM.PrimaryButtonUM(
text = resourceReference(R.string.common_continue),
isEnabled = state.isPrimaryButtonEnabled,
onClick = ::onNextClick,
onClick = {
onNextClick()
params.onNextClick()
},
),
prevButton = null,
secondaryPairButtonsUM = null,