diff --git a/app/src/main/java/com/tangem/tap/common/analytics/converters/ShopOrderToEventConverter.kt b/app/src/main/java/com/tangem/tap/common/analytics/converters/ShopOrderToEventConverter.kt index 5d779e661f..6b8f903fb9 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/converters/ShopOrderToEventConverter.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/converters/ShopOrderToEventConverter.kt @@ -3,7 +3,7 @@ package com.tangem.tap.common.analytics.converters import com.shopify.buy3.Storefront import com.tangem.common.Converter import com.tangem.tap.common.analytics.events.Shop -import com.tangem.tap.common.shop.data.ProductType +import com.tangem.tap.features.shop.domain.models.ProductType /** [REDACTED_AUTHOR] diff --git a/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt b/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt index 3032ef611a..97d9bd093b 100644 --- a/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt +++ b/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt @@ -8,12 +8,13 @@ import com.tangem.core.analytics.Analytics import com.tangem.datasource.config.models.ShopifyShop import com.tangem.tap.common.analytics.converters.ShopOrderToEventConverter import com.tangem.tap.common.extensions.filterNotNull -import com.tangem.tap.common.shop.data.ProductType import com.tangem.tap.common.shop.data.TangemProduct import com.tangem.tap.common.shop.data.TotalSum import com.tangem.tap.common.shop.googlepay.GooglePayService import com.tangem.tap.common.shop.shopify.ShopifyService import com.tangem.tap.common.shop.shopify.data.CheckoutItem +import com.tangem.tap.features.shop.domain.models.ProductType +import com.tangem.tap.features.shop.domain.models.ProductType.Companion.SKUS_TO_DISPLAY import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope @@ -206,12 +207,6 @@ class TangemShopService(application: Application, shopifyShop: ShopifyShop) { } } } - - companion object { - const val TANGEM_WALLET_2_CARDS_SKU = "TG115X2-S" - const val TANGEM_WALLET_3_CARDS_SKU = "TG115X3-S" - val SKUS_TO_DISPLAY = listOf(TANGEM_WALLET_2_CARDS_SKU, TANGEM_WALLET_3_CARDS_SKU) - } } private fun Storefront.MoneyV2.format(): String { diff --git a/app/src/main/java/com/tangem/tap/common/shop/data/ProductType.kt b/app/src/main/java/com/tangem/tap/common/shop/data/ProductType.kt deleted file mode 100644 index 4238b4d883..0000000000 --- a/app/src/main/java/com/tangem/tap/common/shop/data/ProductType.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.tangem.tap.common.shop.data - -import com.tangem.tap.common.shop.TangemShopService - -enum class ProductType(val sku: String) { - WALLET_2_CARDS(TangemShopService.TANGEM_WALLET_2_CARDS_SKU), - WALLET_3_CARDS(TangemShopService.TANGEM_WALLET_3_CARDS_SKU), - ; - - companion object { - fun fromSku(sku: String): ProductType? { - return when (sku) { - WALLET_2_CARDS.sku -> WALLET_2_CARDS - WALLET_3_CARDS.sku -> WALLET_3_CARDS - else -> null - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/shop/data/TangemProduct.kt b/app/src/main/java/com/tangem/tap/common/shop/data/TangemProduct.kt index 79eb72e1f1..9a01a5df51 100644 --- a/app/src/main/java/com/tangem/tap/common/shop/data/TangemProduct.kt +++ b/app/src/main/java/com/tangem/tap/common/shop/data/TangemProduct.kt @@ -1,5 +1,7 @@ package com.tangem.tap.common.shop.data +import com.tangem.tap.features.shop.domain.models.ProductType + data class TangemProduct( val type: ProductType, val totalSum: TotalSum? = null, diff --git a/app/src/main/java/com/tangem/tap/features/shop/data/DefaultShopRepository.kt b/app/src/main/java/com/tangem/tap/features/shop/data/DefaultShopRepository.kt index 74505b69d3..1abbd3d17c 100644 --- a/app/src/main/java/com/tangem/tap/features/shop/data/DefaultShopRepository.kt +++ b/app/src/main/java/com/tangem/tap/features/shop/data/DefaultShopRepository.kt @@ -2,10 +2,13 @@ package com.tangem.tap.features.shop.data import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.api.tangemTech.models.ShopResponse +import com.tangem.domain.common.extensions.withIOContext import com.tangem.tap.features.shop.domain.ShopRepository +import com.tangem.tap.features.shop.domain.models.SalesProduct import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.runCatching import timber.log.Timber +import java.util.Locale /** * Default implementation of shop feature repository @@ -20,6 +23,8 @@ internal class DefaultShopRepository( private val dispatchers: CoroutineDispatcherProvider, ) : ShopRepository { + private val salesProductConverter = SalesProductConverter() + override suspend fun isShopifyOrderingAvailable(): Boolean { return runCatching(dispatchers.io) { tangemTechApi.getShopInfo(name = SHOPIFY_NAME) } .fold( @@ -31,7 +36,24 @@ internal class DefaultShopRepository( ) } + override suspend fun getSalesProductInfo(): List { + return withIOContext { + val salesInfo = tangemTechApi.getSalesInfo(locale = getLocaleName(), shops = SHOPIFY_NAME) + salesProductConverter.convert(salesInfo) + } + } + + private fun getLocaleName(): String { + return if (Locale.getDefault().language == "ru") { + RU_LOCALE + } else { + EN_LOCALE + } + } + private companion object { - const val SHOPIFY_NAME = "shopify" + private const val SHOPIFY_NAME = "shopify" + private const val RU_LOCALE = "ru" + private const val EN_LOCALE = "en" } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/shop/data/SalesProductConverter.kt b/app/src/main/java/com/tangem/tap/features/shop/data/SalesProductConverter.kt new file mode 100644 index 0000000000..9d8f440da6 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/shop/data/SalesProductConverter.kt @@ -0,0 +1,40 @@ +package com.tangem.tap.features.shop.data + +import com.tangem.datasource.api.tangemTech.models.SalesResponse +import com.tangem.tap.features.shop.domain.models.Notification +import com.tangem.tap.features.shop.domain.models.ProductState +import com.tangem.tap.features.shop.domain.models.ProductType +import com.tangem.tap.features.shop.domain.models.SalesProduct +import com.tangem.utils.converter.Converter + +internal class SalesProductConverter : Converter> { + + override fun convert(value: SalesResponse): List { + return value.sales.map { sales -> + val productState = when (sales.state) { + "order" -> ProductState.ORDER + "pre-order" -> ProductState.PRE_ORDER + "sold-out" -> ProductState.SOLD_OUT + else -> ProductState.SOLD_OUT + } + val productType = when (sales.product.code) { + "pack2" -> ProductType.WALLET_2_CARDS + "pack3" -> ProductType.WALLET_3_CARDS + else -> ProductType.WALLET_3_CARDS + } + SalesProduct( + id = sales.id, + productType = productType, + state = productState, + name = sales.product.name, + notification = sales.notification?.let { notification -> + Notification( + type = notification.type, + title = notification.title, + description = notification.description, + ) + }, + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/shop/di/ShopUseCaseModule.kt b/app/src/main/java/com/tangem/tap/features/shop/di/ShopUseCaseModule.kt index 2c00aa1181..a6f22bec79 100644 --- a/app/src/main/java/com/tangem/tap/features/shop/di/ShopUseCaseModule.kt +++ b/app/src/main/java/com/tangem/tap/features/shop/di/ShopUseCaseModule.kt @@ -3,6 +3,8 @@ package com.tangem.tap.features.shop.di import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.tap.features.shop.data.DefaultShopRepository import com.tangem.tap.features.shop.domain.DefaultShopifyOrderingAvailabilityUseCase +import com.tangem.tap.features.shop.domain.GetShopifySalesProductsUseCase +import com.tangem.tap.features.shop.domain.ShopRepository import com.tangem.tap.features.shop.domain.ShopifyOrderingAvailabilityUseCase import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module @@ -17,12 +19,26 @@ internal object ShopUseCaseModule { @Provides @ViewModelScoped - fun provideShopifyOrderingAvailabilityUseCase( - tangemTechApi: TangemTechApi, - dispatchers: CoroutineDispatcherProvider, - ): ShopifyOrderingAvailabilityUseCase { + fun provideShopifyOrderingAvailabilityUseCase(shopRepository: ShopRepository): ShopifyOrderingAvailabilityUseCase { return DefaultShopifyOrderingAvailabilityUseCase( - shopRepository = DefaultShopRepository(tangemTechApi, dispatchers), + shopRepository = shopRepository, ) } + + @Provides + @ViewModelScoped + fun provideGetShopifySalesProductsUseCase(shopRepository: ShopRepository): GetShopifySalesProductsUseCase { + return GetShopifySalesProductsUseCase( + shopRepository = shopRepository, + ) + } + + @Provides + @ViewModelScoped + fun provideDefaultShopRepository( + tangemTechApi: TangemTechApi, + dispatchers: CoroutineDispatcherProvider, + ): ShopRepository { + return DefaultShopRepository(tangemTechApi = tangemTechApi, dispatchers = dispatchers) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/shop/di/ShopifyTogglesModule.kt b/app/src/main/java/com/tangem/tap/features/shop/di/ShopifyTogglesModule.kt new file mode 100644 index 0000000000..97b5839f47 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/shop/di/ShopifyTogglesModule.kt @@ -0,0 +1,23 @@ +package com.tangem.tap.features.shop.di + +import com.tangem.core.featuretoggle.manager.FeatureTogglesManager +import com.tangem.tap.features.shop.toggles.DefaultShopifyFeatureToggleManager +import com.tangem.tap.features.shop.toggles.ShopifyFeatureToggleManager +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object ShopifyTogglesModule { + + @Provides + @Singleton + fun provideDefaultShopifyFeatureToggleManager( + featureToggleManager: FeatureTogglesManager, + ): ShopifyFeatureToggleManager { + return DefaultShopifyFeatureToggleManager(featureToggleManager) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/shop/domain/GetShopifySalesProductsUseCase.kt b/app/src/main/java/com/tangem/tap/features/shop/domain/GetShopifySalesProductsUseCase.kt new file mode 100644 index 0000000000..7881b82dea --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/shop/domain/GetShopifySalesProductsUseCase.kt @@ -0,0 +1,25 @@ +package com.tangem.tap.features.shop.domain + +import arrow.core.Either +import arrow.core.left +import arrow.core.right +import com.tangem.tap.features.shop.domain.models.SalesError +import com.tangem.tap.features.shop.domain.models.SalesProduct + +/** + * Use case to get shopify available products + * + * @property shopRepository shop feature repository + */ +class GetShopifySalesProductsUseCase( + private val shopRepository: ShopRepository, +) { + + suspend operator fun invoke(): Either> { + return try { + shopRepository.getSalesProductInfo().right() + } catch (e: Exception) { + SalesError.DataError(e).left() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/shop/domain/ShopRepository.kt b/app/src/main/java/com/tangem/tap/features/shop/domain/ShopRepository.kt index c8cd6f3780..67803630b8 100644 --- a/app/src/main/java/com/tangem/tap/features/shop/domain/ShopRepository.kt +++ b/app/src/main/java/com/tangem/tap/features/shop/domain/ShopRepository.kt @@ -1,12 +1,17 @@ package com.tangem.tap.features.shop.domain +import com.tangem.tap.features.shop.domain.models.SalesProduct + /** * Shop feature repository * [REDACTED_AUTHOR] */ -internal interface ShopRepository { +interface ShopRepository { /** Get shopify ordering availability */ suspend fun isShopifyOrderingAvailable(): Boolean + + /** Get actual sales product info */ + suspend fun getSalesProductInfo(): List } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/shop/domain/models/ProductType.kt b/app/src/main/java/com/tangem/tap/features/shop/domain/models/ProductType.kt new file mode 100644 index 0000000000..0987fdd8bd --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/shop/domain/models/ProductType.kt @@ -0,0 +1,22 @@ +package com.tangem.tap.features.shop.domain.models + +private const val TANGEM_WALLET_2_CARDS_SKU = "TG115X2-S" +private const val TANGEM_WALLET_3_CARDS_SKU = "TG115X3-S" + +enum class ProductType(val sku: String) { + + WALLET_2_CARDS(TANGEM_WALLET_2_CARDS_SKU), + WALLET_3_CARDS(TANGEM_WALLET_3_CARDS_SKU), + ; + + companion object { + val SKUS_TO_DISPLAY = listOf(TANGEM_WALLET_2_CARDS_SKU, TANGEM_WALLET_3_CARDS_SKU) + fun fromSku(sku: String): ProductType? { + return when (sku) { + WALLET_2_CARDS.sku -> WALLET_2_CARDS + WALLET_3_CARDS.sku -> WALLET_3_CARDS + else -> null + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/shop/domain/models/SalesError.kt b/app/src/main/java/com/tangem/tap/features/shop/domain/models/SalesError.kt new file mode 100644 index 0000000000..373b8d14c8 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/shop/domain/models/SalesError.kt @@ -0,0 +1,5 @@ +package com.tangem.tap.features.shop.domain.models + +sealed class SalesError { + data class DataError(val cause: Throwable) : SalesError() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/shop/domain/models/SalesProduct.kt b/app/src/main/java/com/tangem/tap/features/shop/domain/models/SalesProduct.kt new file mode 100644 index 0000000000..b3b5a083d3 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/shop/domain/models/SalesProduct.kt @@ -0,0 +1,30 @@ +package com.tangem.tap.features.shop.domain.models + +/** + * Sales product + * + * @property id product id + * @property productType shows TW2 cards or 3 cards + * @property state state as order available etc + * @property name product name + * @property notification optional notification + */ +data class SalesProduct( + val id: String, + val productType: ProductType, + val state: ProductState, + val name: String, + val notification: Notification?, +) + +data class Notification( + val type: String, + val title: String, + val description: String, +) + +enum class ProductState { + ORDER, + SOLD_OUT, + PRE_ORDER, +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/shop/presentation/ShopViewModel.kt b/app/src/main/java/com/tangem/tap/features/shop/presentation/ShopViewModel.kt index 34966466cc..2e39e2bd4f 100644 --- a/app/src/main/java/com/tangem/tap/features/shop/presentation/ShopViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/shop/presentation/ShopViewModel.kt @@ -2,6 +2,7 @@ package com.tangem.tap.features.shop.presentation import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.tangem.tap.features.shop.domain.GetShopifySalesProductsUseCase import com.tangem.tap.features.shop.domain.ShopifyOrderingAvailabilityUseCase import com.tangem.tap.features.shop.redux.ShopAction import com.tangem.tap.proxy.AppStateHolder @@ -15,6 +16,7 @@ import javax.inject.Inject * Shop screen view model * * @property shopifyOrderingAvailabilityUseCase use case to define shopify ordering availability + * @property getShopifySalesProductsUseCase use case to get actual sales info * @property dispatchers coroutine dispatchers provider * @property appStateHolder redux state holder * @@ -23,6 +25,7 @@ import javax.inject.Inject @HiltViewModel internal class ShopViewModel @Inject constructor( private val shopifyOrderingAvailabilityUseCase: ShopifyOrderingAvailabilityUseCase, + private val getShopifySalesProductsUseCase: GetShopifySalesProductsUseCase, private val dispatchers: CoroutineDispatcherProvider, private val appStateHolder: AppStateHolder, ) : ViewModel() { @@ -36,4 +39,22 @@ internal class ShopViewModel @Inject constructor( appStateHolder.mainStore?.dispatch(action = ShopAction.SetOrderingDelayBlockVisibility(visibility)) } } + + /** + * Get actual sales products info + * to configure view dynamically + */ + fun getActualSalesInfo() { + viewModelScope.launch(dispatchers.main) { + val action = getShopifySalesProductsUseCase().fold( + ifLeft = { + ShopAction.SalesProductsError + }, + ifRight = { + ShopAction.SalesProductsLoaded(it) + }, + ) + appStateHolder.mainStore?.dispatch(action) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/shop/redux/ShopAction.kt b/app/src/main/java/com/tangem/tap/features/shop/redux/ShopAction.kt index 54c6bcad43..a72324e845 100644 --- a/app/src/main/java/com/tangem/tap/features/shop/redux/ShopAction.kt +++ b/app/src/main/java/com/tangem/tap/features/shop/redux/ShopAction.kt @@ -2,9 +2,10 @@ package com.tangem.tap.features.shop.redux import android.content.Intent import com.tangem.tap.common.redux.NotificationAction -import com.tangem.tap.common.shop.data.ProductType +import com.tangem.tap.features.shop.domain.models.ProductType import com.tangem.tap.common.shop.data.TangemProduct import com.tangem.tap.common.shop.googlepay.GooglePayService +import com.tangem.tap.features.shop.domain.models.SalesProduct import com.tangem.wallet.R import org.rekotlin.Action @@ -44,4 +45,8 @@ sealed interface ShopAction : Action { object ResetState : ShopAction data class SetOrderingDelayBlockVisibility(val visibility: Boolean) : ShopAction + + data class SalesProductsLoaded(val salesProducts: List) : ShopAction + + object SalesProductsError : ShopAction } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/shop/redux/ShopReducer.kt b/app/src/main/java/com/tangem/tap/features/shop/redux/ShopReducer.kt index 60cb0667ad..11061c7abf 100644 --- a/app/src/main/java/com/tangem/tap/features/shop/redux/ShopReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/shop/redux/ShopReducer.kt @@ -39,5 +39,11 @@ private fun internalReduce(action: Action, state: ShopState): ShopState { is ShopAction.FinishSuccessfulOrder, is ShopAction.LoadProducts.Failure, -> state + is ShopAction.SalesProductsLoaded -> state.copy( + salesProducts = action.salesProducts, + ) + is ShopAction.SalesProductsError -> state.copy( + salesProducts = emptyList(), + ) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/shop/redux/ShopState.kt b/app/src/main/java/com/tangem/tap/features/shop/redux/ShopState.kt index 491e961e58..08bac48f4d 100644 --- a/app/src/main/java/com/tangem/tap/features/shop/redux/ShopState.kt +++ b/app/src/main/java/com/tangem/tap/features/shop/redux/ShopState.kt @@ -1,12 +1,14 @@ package com.tangem.tap.features.shop.redux -import com.tangem.tap.common.shop.data.ProductType +import com.tangem.tap.features.shop.domain.models.ProductType import com.tangem.tap.common.shop.data.TangemProduct +import com.tangem.tap.features.shop.domain.models.SalesProduct import org.rekotlin.StateType data class ShopState( val availableProducts: List = emptyList(), val selectedProduct: ProductType = ProductType.WALLET_3_CARDS, + val salesProducts: List = emptyList(), val promoCode: String? = null, val promoCodeLoading: Boolean = false, val isGooglePayAvailable: Boolean = false, // TODO: change when we add support for GPay diff --git a/app/src/main/java/com/tangem/tap/features/shop/toggles/DefaultShopifyFeatureToggleManager.kt b/app/src/main/java/com/tangem/tap/features/shop/toggles/DefaultShopifyFeatureToggleManager.kt new file mode 100644 index 0000000000..eda902f707 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/shop/toggles/DefaultShopifyFeatureToggleManager.kt @@ -0,0 +1,11 @@ +package com.tangem.tap.features.shop.toggles + +import com.tangem.core.featuretoggle.manager.FeatureTogglesManager + +internal class DefaultShopifyFeatureToggleManager( + private val featureTogglesManager: FeatureTogglesManager, +) : ShopifyFeatureToggleManager { + + override val isDynamicSalesProductsEnabled: Boolean + get() = featureTogglesManager.isFeatureEnabled("SHOPIFY_DYNAMIC_ENABLED") +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/shop/toggles/ShopifyFeatureToggleManager.kt b/app/src/main/java/com/tangem/tap/features/shop/toggles/ShopifyFeatureToggleManager.kt new file mode 100644 index 0000000000..ab3b8fc968 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/shop/toggles/ShopifyFeatureToggleManager.kt @@ -0,0 +1,10 @@ +package com.tangem.tap.features.shop.toggles + +/** + * Shopify feature toggle manager that provides info about shopify toggle availability + * + */ +interface ShopifyFeatureToggleManager { + + val isDynamicSalesProductsEnabled: Boolean +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt b/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt index 4af0db0285..58b89b9901 100644 --- a/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt @@ -17,20 +17,27 @@ import com.tangem.tap.common.KeyboardObserver import com.tangem.tap.common.extensions.getQuantityString import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show -import com.tangem.tap.common.shop.data.ProductType import com.tangem.tap.features.BaseStoreFragment +import com.tangem.tap.features.shop.domain.models.ProductState +import com.tangem.tap.features.shop.domain.models.ProductType +import com.tangem.tap.features.shop.domain.models.SalesProduct import com.tangem.tap.features.shop.presentation.ShopViewModel import com.tangem.tap.features.shop.redux.ShopAction import com.tangem.tap.features.shop.redux.ShopState +import com.tangem.tap.features.shop.toggles.ShopifyFeatureToggleManager import com.tangem.tap.store import com.tangem.wallet.R import com.tangem.wallet.databinding.FragmentShopBinding import dagger.hilt.android.AndroidEntryPoint import org.rekotlin.StoreSubscriber +import javax.inject.Inject @AndroidEntryPoint internal class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSubscriber { + @Inject + lateinit var shopifyFeatureToggleManager: ShopifyFeatureToggleManager + private val binding: FragmentShopBinding by viewBinding(FragmentShopBinding::bind) private var cardTranslationY = 70f @@ -50,7 +57,11 @@ internal class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSu override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - viewModel.checkOrderingDelayBlockVisibility() + if (shopifyFeatureToggleManager.isDynamicSalesProductsEnabled) { + viewModel.getActualSalesInfo() + } else { + viewModel.checkOrderingDelayBlockVisibility() + } activity?.onBackPressedDispatcher?.addCallback( this, @@ -142,7 +153,11 @@ internal class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSu animateProductSelection(state.selectedProduct) handlePriceState(state) handlePromoCodeState(state) - handleOrderingDelayBlock(isVisible = state.isOrderingDelayBlockVisible) + if (shopifyFeatureToggleManager.isDynamicSalesProductsEnabled) { + handleNotificationBlock(state) + } else { + handleOrderingDelayBlock(isVisible = state.isOrderingDelayBlockVisible) + } handleButtonsState(state) } @@ -187,6 +202,17 @@ internal class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSu if (isVisible) binding.tvSoldOutDesc.show() else binding.tvSoldOutDesc.hide() } + private fun handleNotificationBlock(state: ShopState) { + if (isVisible) { + binding.tvSoldOutDesc.show() + getSelectedSalesProduct(state)?.notification?.let { notification -> + binding.tvSoldOutDesc.text = notification.description + } + } else { + binding.tvSoldOutDesc.hide() + } + } + private fun handleButtonsState(state: ShopState) = with(binding) { btnPayGooglePay.root.show(state.isGooglePayAvailable) btnAlternativePayment.show(state.isGooglePayAvailable) @@ -194,8 +220,15 @@ internal class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSu if (state.total != null) { btnAlternativePayment.setOnClickListener { store.dispatch(ShopAction.StartWebCheckout) } - btnMainAction.setOnClickListener { store.dispatch(ShopAction.StartWebCheckout) } btnPayGooglePay.root.setOnClickListener { store.dispatch(ShopAction.BuyWithGooglePay) } + btnMainAction.setOnClickListener { store.dispatch(ShopAction.StartWebCheckout) } + if (state.salesProducts.isNotEmpty()) { + getSelectedSalesProduct(state)?.let { selectedProduct -> + btnMainAction.text = getMainBtnTextByProductState( + productState = selectedProduct.state, + ) + } + } } } @@ -203,4 +236,16 @@ internal class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSu store.dispatch(ShopAction.ResetState) super.handleOnBackPressed() } + + private fun getSelectedSalesProduct(state: ShopState): SalesProduct? { + return state.salesProducts.find { + it.productType == state.selectedProduct + } + } + + private fun getMainBtnTextByProductState(productState: ProductState): String = when (productState) { + ProductState.ORDER -> getString(R.string.shop_buy_now) + ProductState.SOLD_OUT -> "Sold out" // getString(R.string.sold_out) // todo finalize in next PR + ProductState.PRE_ORDER -> "Pre order" // getString(R.string.pre_order) // todo finalize in next PR + } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt index 992f6d0eb5..e75594060f 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt @@ -54,4 +54,10 @@ interface TangemTechApi { @GET("shops") suspend fun getShopInfo(@Query(value = "name") name: String): ShopResponse + + @GET("sales") + suspend fun getSalesInfo( + @Query(value = "locale") locale: String, + @Query(value = "shops") shops: String, + ): SalesResponse } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/SalesResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/SalesResponse.kt new file mode 100644 index 0000000000..1c3461f012 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/SalesResponse.kt @@ -0,0 +1,44 @@ +package com.tangem.datasource.api.tangemTech.models + +import com.squareup.moshi.Json + +/** + * Sales response + */ +data class SalesResponse( + @Json(name = "sales") val sales: List, +) + +/** + * Sales info + * + * @property id sales id + * @property state state as order, sold-out, pre-order + * @property product product that is sales + * @property notification optional notification for product + */ +data class Sales( + @Json(name = "id") val id: String, + @Json(name = "state") val state: String, + @Json(name = "product") val product: Product, + @Json(name = "notification") val notification: Notification?, +) + +/** + * Product + * + * @property id product id + * @property code code that shows what product it is + * @property name product name + */ +data class Product( + @Json(name = "id") val id: String, + @Json(name = "code") val code: String, + @Json(name = "name") val name: String, +) + +data class Notification( + @Json(name = "type") val type: String, + @Json(name = "title") val title: String, + @Json(name = "description") val description: String, +) \ No newline at end of file diff --git a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json index b5005743ed..b2d4da3602 100644 --- a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json @@ -22,5 +22,9 @@ { "name": "REDESIGNED_TOKEN_DETAIL_SCREEN_ENABLED", "version": "undefined" + }, + { + "name": "SHOPIFY_DYNAMIC_ENABLED", + "version": "undefined" } ]