diff --git a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json index c6722f2a85..a219ffaceb 100644 --- a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json @@ -70,5 +70,9 @@ { "name": "SWAP_INTEGRATED_APPROVE", "version": "undefined" + }, + { + "name": "SWAP_AB_ENABLED", + "version": "undefined" } ] diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt index f03cdfb336..f99dbf63f7 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt @@ -59,6 +59,8 @@ object PreferencesKeys { val LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY by lazy { stringPreferencesKey(name = "lastSwappedCryptoCurrency") } + val SWAP_UI_MODE_KEY by lazy { stringPreferencesKey(name = "swapUiMode") } + val WAS_TWINS_ONBOARDING_SHOWN by lazy { booleanPreferencesKey(name = "twinsOnboardingShown") } val IS_TANGEM_TOS_ACCEPTED_KEY by lazy { booleanPreferencesKey(name = "tangem_tos_accepted") } diff --git a/features/swap/api/src/main/kotlin/com/tangem/features/swap/SwapFeatureToggles.kt b/features/swap/api/src/main/kotlin/com/tangem/features/swap/SwapFeatureToggles.kt index 5c9df6c93e..38e6caa619 100644 --- a/features/swap/api/src/main/kotlin/com/tangem/features/swap/SwapFeatureToggles.kt +++ b/features/swap/api/src/main/kotlin/com/tangem/features/swap/SwapFeatureToggles.kt @@ -3,4 +3,5 @@ package com.tangem.features.swap interface SwapFeatureToggles { val isSwapSwitchToTransferEnabled: Boolean val isSwapIntegratedApproveEnabled: Boolean + val isSwapAbEnabled: Boolean } \ No newline at end of file diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt index f2c9cfd2af..2f5e08ccf7 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt @@ -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) + } } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/GetSwapUiModeUseCase.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/GetSwapUiModeUseCase.kt new file mode 100644 index 0000000000..68533c7a1f --- /dev/null +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/GetSwapUiModeUseCase.kt @@ -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 + } +} \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt index 9a38c5448b..fbc9f2828a 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt @@ -75,4 +75,6 @@ interface SwapRepository { txHash: String, payInExtraId: String?, ): Either + + suspend fun getStoredSwapUiMode(): SwapUIMode? } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt index c753a3381d..0e96260f26 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt @@ -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 diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapUIMode.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapUIMode.kt new file mode 100644 index 0000000000..f865c32424 --- /dev/null +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapUIMode.kt @@ -0,0 +1,6 @@ +package com.tangem.feature.swap.domain.models.domain + +enum class SwapUIMode { + Simple, + Detailed, +} \ No newline at end of file diff --git a/features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/GetSwapUiModeUseCaseTest.kt b/features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/GetSwapUiModeUseCaseTest.kt new file mode 100644 index 0000000000..cc65da1b9b --- /dev/null +++ b/features/swap/domain/src/test/kotlin/com/tangem/feature/swap/domain/GetSwapUiModeUseCaseTest.kt @@ -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) + } +} \ No newline at end of file diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/DefaultSwapFeatureToggles.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/DefaultSwapFeatureToggles.kt index bb5bc7efb5..368ed8672f 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/DefaultSwapFeatureToggles.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/DefaultSwapFeatureToggles.kt @@ -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, + ) } \ No newline at end of file diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt index 5bad26508b..36da88d7be 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt @@ -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() @@ -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() { diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt index 95f8c7c400..3afeda4ddc 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt @@ -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, diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index 814623edd8..df3ed76fee 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -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, ) }