Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-07 18:47:43 +04:00
parent ed11011f07
commit a8744eb429
13 changed files with 130 additions and 1 deletions

View file

@ -3,4 +3,5 @@ package com.tangem.features.swap
interface SwapFeatureToggles {
val isSwapSwitchToTransferEnabled: Boolean
val isSwapIntegratedApproveEnabled: Boolean
val isSwapAbEnabled: Boolean
}

View file

@ -19,6 +19,8 @@ import com.tangem.datasource.api.express.models.response.TxDetails
import com.tangem.datasource.crypto.DataSignatureVerifier
import com.tangem.datasource.exchangeservice.swap.ExpressUtils
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.express.models.ExpressOperationType
import com.tangem.domain.models.currency.CryptoCurrency
@ -413,4 +415,8 @@ internal class DefaultSwapRepository(
ExpressDataError.UnknownError
}
}
override suspend fun getStoredSwapUiMode(): SwapUIMode? {
return appPreferencesStore.getObjectSyncOrNull(PreferencesKeys.SWAP_UI_MODE_KEY)
}
}

View file

@ -0,0 +1,18 @@
package com.tangem.feature.swap.domain
import com.tangem.feature.swap.domain.api.SwapRepository
import com.tangem.feature.swap.domain.models.domain.SwapUIMode
import com.tangem.features.swap.SwapFeatureToggles
class GetSwapUiModeUseCase(
private val swapFeatureToggles: SwapFeatureToggles,
private val swapRepository: SwapRepository,
) {
suspend operator fun invoke(): SwapUIMode {
if (!swapFeatureToggles.isSwapAbEnabled) return SwapUIMode.Detailed
// TODO: take default from Amplitude (true -> Detailed, false -> Simple).
// Until then default is Detailed.
return swapRepository.getStoredSwapUiMode() ?: SwapUIMode.Detailed
}
}

View file

@ -75,4 +75,6 @@ interface SwapRepository {
txHash: String,
payInExtraId: String?,
): Either<ExpressDataError, Unit>
suspend fun getStoredSwapUiMode(): SwapUIMode?
}

View file

@ -2,8 +2,11 @@ package com.tangem.feature.swap.domain.di
import com.tangem.feature.swap.domain.AllowPermissionsHandler
import com.tangem.feature.swap.domain.AllowPermissionsHandlerImpl
import com.tangem.feature.swap.domain.GetSwapUiModeUseCase
import com.tangem.feature.swap.domain.SwapInteractor
import com.tangem.feature.swap.domain.SwapInteractorImpl
import com.tangem.feature.swap.domain.api.SwapRepository
import com.tangem.features.swap.SwapFeatureToggles
import dagger.Binds
import dagger.Module
import dagger.Provides
@ -20,6 +23,16 @@ internal class SwapDomainModule {
fun provideAllowPermissionsHandler(): AllowPermissionsHandler {
return AllowPermissionsHandlerImpl()
}
@Provides
@Singleton
fun provideGetSwapUiModeUseCase(
swapFeatureToggles: SwapFeatureToggles,
swapRepository: SwapRepository,
): GetSwapUiModeUseCase = GetSwapUiModeUseCase(
swapFeatureToggles = swapFeatureToggles,
swapRepository = swapRepository,
)
}
@Module

View file

@ -0,0 +1,6 @@
package com.tangem.feature.swap.domain.models.domain
enum class SwapUIMode {
Simple,
Detailed,
}

View file

@ -0,0 +1,62 @@
package com.tangem.feature.swap.domain
import com.google.common.truth.Truth.assertThat
import com.tangem.feature.swap.domain.api.SwapRepository
import com.tangem.feature.swap.domain.models.domain.SwapUIMode
import com.tangem.features.swap.SwapFeatureToggles
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
internal class GetSwapUiModeUseCaseTest {
private val swapFeatureToggles: SwapFeatureToggles = mockk()
private val swapRepository: SwapRepository = mockk()
private val sut = GetSwapUiModeUseCase(
swapFeatureToggles = swapFeatureToggles,
swapRepository = swapRepository,
)
@Test
fun `GIVEN feature toggle is disabled WHEN invoke THEN returns Detailed without reading repository`() = runTest {
coEvery { swapFeatureToggles.isSwapAbEnabled } returns false
val actual = sut.invoke()
assertThat(actual).isEqualTo(SwapUIMode.Detailed)
coVerify(exactly = 0) { swapRepository.getStoredSwapUiMode() }
}
@Test
fun `GIVEN toggle enabled and repository has Detailed WHEN invoke THEN returns Detailed`() = runTest {
coEvery { swapFeatureToggles.isSwapAbEnabled } returns true
coEvery { swapRepository.getStoredSwapUiMode() } returns SwapUIMode.Detailed
val actual = sut.invoke()
assertThat(actual).isEqualTo(SwapUIMode.Detailed)
}
@Test
fun `GIVEN toggle enabled and repository has Simple WHEN invoke THEN returns Simple`() = runTest {
coEvery { swapFeatureToggles.isSwapAbEnabled } returns true
coEvery { swapRepository.getStoredSwapUiMode() } returns SwapUIMode.Simple
val actual = sut.invoke()
assertThat(actual).isEqualTo(SwapUIMode.Simple)
}
@Test
fun `GIVEN toggle enabled and repository has no value WHEN invoke THEN returns Detailed`() = runTest {
coEvery { swapFeatureToggles.isSwapAbEnabled } returns true
coEvery { swapRepository.getStoredSwapUiMode() } returns null
val actual = sut.invoke()
assertThat(actual).isEqualTo(SwapUIMode.Detailed)
}
}

View file

@ -12,7 +12,12 @@ internal class DefaultSwapFeatureToggles @Inject constructor(
override val isSwapSwitchToTransferEnabled: Boolean = featureTogglesManager.isFeatureEnabled(
toggle = FeatureToggles.SWAP_SWITCH_TO_TRANSFER_ENABLED,
)
override val isSwapIntegratedApproveEnabled: Boolean = featureTogglesManager.isFeatureEnabled(
toggle = FeatureToggles.SWAP_INTEGRATED_APPROVE,
)
override val isSwapAbEnabled: Boolean = featureTogglesManager.isFeatureEnabled(
toggle = FeatureToggles.SWAP_AB_ENABLED,
)
}

View file

@ -73,6 +73,7 @@ import com.tangem.feature.swap.analytics.SwapQuotePerformanceTracker
import com.tangem.feature.swap.component.SwapFeeSelectorBlockComponent
import com.tangem.feature.swap.converters.SwapTransactionErrorStateConverter
import com.tangem.feature.swap.domain.AllowPermissionsHandler
import com.tangem.feature.swap.domain.GetSwapUiModeUseCase
import com.tangem.feature.swap.domain.SwapInteractor
import com.tangem.feature.swap.domain.TransactionFeeResult
import com.tangem.feature.swap.domain.TxFeeSealedState
@ -153,6 +154,7 @@ internal class SwapModel @Inject constructor(
private val initialCurrenciesResolver: InitialCurrenciesResolver,
private val allowPermissionsHandler: AllowPermissionsHandler,
private val swapFeatureToggles: SwapFeatureToggles,
private val getSwapUiModeUseCase: GetSwapUiModeUseCase,
) : Model() {
private val params = paramsContainer.require<SwapComponent.Params>()
@ -284,6 +286,10 @@ internal class SwapModel @Inject constructor(
isBalanceHidden = settings.isBalanceHidden
uiState = stateBuilder.updateBalanceHiddenState(uiState, isBalanceHidden)
}.launchIn(modelScope)
modelScope.launch {
uiState = uiState.copy(swapUIMode = getSwapUiModeUseCase())
}
}
fun onStart() {

View file

@ -8,6 +8,7 @@ import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.extensions.TextReference
import com.tangem.feature.swap.domain.models.domain.SwapUIMode
import com.tangem.feature.swap.domain.models.ui.PriceImpact
import com.tangem.feature.swap.models.states.FeeItemState
import com.tangem.feature.swap.models.states.ProviderState
@ -31,6 +32,7 @@ internal data class SwapStateHolder(
val swapButton: SwapButton,
val shouldShowMaxAmount: Boolean,
val tosState: TosState? = null,
val swapUIMode: SwapUIMode = SwapUIMode.Detailed,
val onRefresh: () -> Unit,
val onBackClicked: () -> Unit,

View file

@ -29,6 +29,7 @@ import com.tangem.feature.swap.domain.models.domain.ExchangeProviderType
import com.tangem.feature.swap.domain.models.domain.IncludeFeeInAmount
import com.tangem.feature.swap.domain.models.domain.RateType
import com.tangem.feature.swap.domain.models.domain.SwapProvider
import com.tangem.feature.swap.domain.models.domain.SwapUIMode
import com.tangem.feature.swap.domain.models.ui.*
import com.tangem.feature.swap.model.SwapNotificationsFactory
import com.tangem.feature.swap.model.SwapProcessDataState
@ -65,7 +66,7 @@ internal class StateBuilder(
SwapNotificationsFactory(actions, isGaslessFeeSupportedForNetwork)
}
fun createInitialLoadingState(): SwapStateHolder {
fun createInitialLoadingState(swapUIMode: SwapUIMode = SwapUIMode.Detailed): SwapStateHolder {
return SwapStateHolder(
sendCardData = getEmptyCardState(
isFromCard = true,
@ -95,6 +96,7 @@ internal class StateBuilder(
shouldShowMaxAmount = false,
priceImpact = PriceImpact.Empty,
isInsufficientFunds = false,
swapUIMode = swapUIMode,
)
}