From 53e29be3c29dc833f99d7d0b75cdd899f1f90d1b Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 10 Feb 2025 11:01:12 +0500 Subject: [PATCH 1/4] Updated on 2026-08-14 --- .../buttons/HorizontalActionChips.kt | 32 ++++++++++++------- .../buttons/actions/ActionButtonConfig.kt | 3 ++ .../ui/components/buttons/actions/Actions.kt | 28 ++++++++++++++++ .../ui/components/TokenDetailsBalanceBlock.kt | 1 + 4 files changed, 52 insertions(+), 12 deletions(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/HorizontalActionChips.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/HorizontalActionChips.kt index 715a630ed3..c7f0ba8246 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/HorizontalActionChips.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/HorizontalActionChips.kt @@ -4,20 +4,23 @@ import android.content.res.Configuration import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import androidx.compose.ui.unit.dp import com.tangem.core.ui.R import com.tangem.core.ui.components.buttons.actions.ActionButton import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.extensions.TextReference -import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -25,6 +28,7 @@ import kotlinx.collections.immutable.persistentListOf fun HorizontalActionChips( buttons: ImmutableList, modifier: Modifier = Modifier, + containerColor: Color = TangemTheme.colors.background.secondary, contentPadding: PaddingValues = PaddingValues(TangemTheme.dimens.spacing0), ) { LazyRow( @@ -36,7 +40,7 @@ fun HorizontalActionChips( // do not use key cause when change items order, list is scrolled items( items = buttons, - itemContent = { ActionButton(config = it) }, + itemContent = { ActionButton(config = it, containerColor = containerColor) }, ) } } @@ -46,22 +50,31 @@ fun HorizontalActionChips( @Preview(widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable private fun Preview_HorizontalActionChips( - @PreviewParameter(ActionButtonConfigProvider::class) buttons: HorizontalActionChips, + @PreviewParameter(ActionButtonConfigProvider::class) buttons: HorizontalActionChipsData, ) { TangemThemePreview { - HorizontalActionChips(buttons = buttons.buttons) + HorizontalActionChips( + buttons = buttons.buttons, + modifier = Modifier.padding(4.dp), + ) } } -private class ActionButtonConfigProvider : CollectionPreviewParameterProvider( +private class ActionButtonConfigProvider : CollectionPreviewParameterProvider( collection = listOf( - HorizontalActionChips( + HorizontalActionChipsData( buttons = persistentListOf( ActionButtonConfig( text = TextReference.Str(value = "Buy"), iconResId = R.drawable.ic_plus_24, onClick = {}, ), + ActionButtonConfig( + text = TextReference.Str(value = "Exchange"), + iconResId = R.drawable.ic_exchange_vertical_24, + onClick = {}, + showBadge = true, + ), ActionButtonConfig( text = TextReference.Str(value = "Send"), iconResId = R.drawable.ic_arrow_up_24, @@ -72,17 +85,12 @@ private class ActionButtonConfigProvider : CollectionPreviewParameterProvider, ) // endregion Preview \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/ActionButtonConfig.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/ActionButtonConfig.kt index d315a2a715..290b811544 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/ActionButtonConfig.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/ActionButtonConfig.kt @@ -13,6 +13,8 @@ import com.tangem.core.ui.extensions.TextReference * @property enabled enabled * @property dimContent determines whether the button content will be dimmed. This property will be ignored if [enabled] * is `false`. + * @property isInProgress indicates progress state of button + * @property showBadge display dot in upper right corner * [REDACTED_AUTHOR] */ @@ -24,4 +26,5 @@ data class ActionButtonConfig( val enabled: Boolean = true, val dimContent: Boolean = false, val isInProgress: Boolean = false, + val showBadge: Boolean = false, ) \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/Actions.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/Actions.kt index d5d30f90ca..63807a2a7d 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/Actions.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/Actions.kt @@ -17,7 +17,10 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow @@ -29,6 +32,7 @@ import androidx.compose.ui.unit.dp import com.tangem.core.ui.R import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview @@ -81,6 +85,7 @@ fun ActionButton( config: ActionButtonConfig, modifier: Modifier = Modifier, color: Color = TangemTheme.colors.button.secondary, + containerColor: Color = TangemTheme.colors.background.secondary, ) { ActionBaseButton( config = config, @@ -97,6 +102,7 @@ fun ActionButton( }, modifier = modifier, color = color, + containerColor = containerColor, ) } @@ -108,6 +114,7 @@ fun ActionBaseButton( content: @Composable (modifier: Modifier) -> Unit, modifier: Modifier = Modifier, color: Color = TangemTheme.colors.button.secondary, + containerColor: Color = TangemTheme.colors.background.secondary, ) { val context = LocalContext.current val backgroundColor by animateColorAsState( @@ -119,6 +126,12 @@ fun ActionBaseButton( modifier = modifier .heightIn(min = 36.dp) .widthIn(min = 100.dp) + .drawWithContent { + drawContent() + if (config.showBadge) { + drawBadge(containerColor = containerColor) + } + } .clip(shape) .combinedClickable( enabled = config.enabled, @@ -212,6 +225,20 @@ fun getTextColor(config: ActionButtonConfig): Color { } } +private fun DrawScope.drawBadge(containerColor: Color) { + val width = size.width + drawCircle( + color = containerColor, + center = Offset(x = width - 2.dp.toPx(), y = 2.dp.toPx()), + radius = 5.dp.toPx(), + ) + drawCircle( + color = TangemColorPalette.Azure, + center = Offset(x = width - 2.dp.toPx(), y = 2.dp.toPx()), + radius = 3.dp.toPx(), + ) +} + @Preview(group = "RoundedActionButton", showBackground = true) @Preview(group = "RoundedActionButton", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable @@ -237,6 +264,7 @@ private class ActionStateProvider : CollectionPreviewParameterProvider Date: Mon, 10 Feb 2025 11:07:39 +0500 Subject: [PATCH 2/4] Updated on 2026-08-14 --- app/build.gradle.kts | 2 + .../tangem/tap/routing/utils/ChildFactory.kt | 22 +++++++ .../com/tangem/common/routing/AppRoute.kt | 8 +++ common/ui/build.gradle.kts | 1 + .../swapStoriesScreen/SwapStoriesFactory.kt | 48 ++++++++++++++ .../ui/swapStoriesScreen/SwapStoriesUM.kt | 26 ++++++++ features/stories/api/.gitignore | 1 + features/stories/api/build.gradle.kts | 20 ++++++ .../feature/stories/api/StoriesComponent.kt | 15 +++++ features/stories/impl/.gitignore | 1 + features/stories/impl/build.gradle.kts | 43 +++++++++++++ .../stories/impl/DefaultStoriesComponent.kt | 34 ++++++++++ .../stories/impl/di/StoriesComponentModule.kt | 18 ++++++ .../stories/impl/di/StoriesModelModule.kt | 20 ++++++ .../stories/impl/model/StoriesModel.kt | 64 +++++++++++++++++++ settings.gradle.kts | 3 + 16 files changed, 326 insertions(+) create mode 100644 common/ui/src/main/java/com/tangem/common/ui/swapStoriesScreen/SwapStoriesFactory.kt create mode 100644 common/ui/src/main/java/com/tangem/common/ui/swapStoriesScreen/SwapStoriesUM.kt create mode 100644 features/stories/api/.gitignore create mode 100644 features/stories/api/build.gradle.kts create mode 100644 features/stories/api/src/main/java/com/tangem/feature/stories/api/StoriesComponent.kt create mode 100644 features/stories/impl/.gitignore create mode 100644 features/stories/impl/build.gradle.kts create mode 100644 features/stories/impl/src/main/java/com/tangem/feature/stories/impl/DefaultStoriesComponent.kt create mode 100644 features/stories/impl/src/main/java/com/tangem/feature/stories/impl/di/StoriesComponentModule.kt create mode 100644 features/stories/impl/src/main/java/com/tangem/feature/stories/impl/di/StoriesModelModule.kt create mode 100644 features/stories/impl/src/main/java/com/tangem/feature/stories/impl/model/StoriesModel.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2f2510944d..a990a9f3d7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -177,6 +177,8 @@ dependencies { implementation(projects.features.onramp.impl) implementation(projects.features.onboardingV2.api) implementation(projects.features.onboardingV2.impl) + implementation(projects.features.stories.api) + implementation(projects.features.stories.impl) /** AndroidX libraries */ implementation(deps.androidx.core.ktx) diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index 276794edab..47c916c3b6 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -4,6 +4,7 @@ import com.tangem.common.routing.AppRoute import com.tangem.core.decompose.context.AppComponentContext import com.tangem.feature.qrscanning.QrScanningRouter import com.tangem.feature.referral.ReferralFragment +import com.tangem.feature.stories.api.StoriesComponent import com.tangem.feature.swap.presentation.SwapFragment import com.tangem.feature.walletsettings.component.WalletSettingsComponent import com.tangem.features.details.component.DetailsComponent @@ -56,6 +57,7 @@ internal class ChildFactory @Inject constructor( private val swapSelectTokensComponentFactory: SwapSelectTokensComponent.Factory, private val onboardingEntryComponentFactory: OnboardingEntryComponent.Factory, private val welcomeComponentFactory: WelcomeComponent.Factory, + private val storiesComponentFactory: StoriesComponent.Factory, private val sendRouter: SendRouter, private val tokenDetailsRouter: TokenDetailsRouter, private val walletRouter: WalletRouter, @@ -199,6 +201,16 @@ internal class ChildFactory @Inject constructor( componentFactory = onboardingEntryComponentFactory, ) } + is AppRoute.Stories -> { + createComponentChild( + contextProvider = contextProvider(route, contextFactory), + params = StoriesComponent.Params( + storyId = route.storyId, + nextScreen = route.nextScreen, + ), + componentFactory = storiesComponentFactory, + ) + } is AppRoute.AccessCodeRecovery, is AppRoute.AppCurrencySelector, is AppRoute.SaveWallet, @@ -407,6 +419,16 @@ internal class ChildFactory @Inject constructor( componentFactory = onboardingEntryComponentFactory, ) } + is AppRoute.Stories -> { + route.asComponentChild( + contextProvider = contextProvider(route, contextFactory), + params = StoriesComponent.Params( + storyId = route.storyId, + nextScreen = route.nextScreen, + ), + componentFactory = storiesComponentFactory, + ) + } } // endregion } diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index c81997cf54..28d839feba 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -341,4 +341,12 @@ sealed class AppRoute(val path: String) : Route { AddBackup, // continue backup process for existing wallet 1 } } + + @Serializable + data class Stories( + val storyId: String, + val nextScreen: AppRoute, + ) : AppRoute(path = "/stories$storyId"), RouteBundleParams { + override fun getBundle(): Bundle = bundle(serializer()) + } } \ No newline at end of file diff --git a/common/ui/build.gradle.kts b/common/ui/build.gradle.kts index 28e0387959..709c410a9f 100644 --- a/common/ui/build.gradle.kts +++ b/common/ui/build.gradle.kts @@ -36,6 +36,7 @@ dependencies { implementation(projects.domain.transaction.models) implementation(projects.domain.wallets.models) implementation(projects.domain.onramp.models) + implementation(projects.domain.promo.models) implementation(deps.tangem.card.core) implementation(deps.tangem.blockchain) { diff --git a/common/ui/src/main/java/com/tangem/common/ui/swapStoriesScreen/SwapStoriesFactory.kt b/common/ui/src/main/java/com/tangem/common/ui/swapStoriesScreen/SwapStoriesFactory.kt new file mode 100644 index 0000000000..c9672cb67a --- /dev/null +++ b/common/ui/src/main/java/com/tangem/common/ui/swapStoriesScreen/SwapStoriesFactory.kt @@ -0,0 +1,48 @@ +package com.tangem.common.ui.swapStoriesScreen + +import com.tangem.common.ui.R +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.domain.promo.models.StoryContent +import kotlinx.collections.immutable.persistentListOf + +object SwapStoriesFactory { + + // WARNING! Be careful with indices. Temporary solution. + // Use all data from v1/stories api (image url, title, subtitle) + @Suppress("MagicNumber") + fun createStoriesState(swapStory: StoryContent, onStoriesClose: () -> Unit): SwapStoriesUM { + val storyOrderedImageUrls = swapStory.getImageUrls() + if (storyOrderedImageUrls.size != 5) return SwapStoriesUM.Empty + + return SwapStoriesUM.Content( + stories = persistentListOf( + SwapStoriesUM.Content.Config( + imageUrl = storyOrderedImageUrls[0], + title = resourceReference(R.string.swap_story_first_title), + subtitle = resourceReference(R.string.swap_story_first_subtitle), + ), + SwapStoriesUM.Content.Config( + imageUrl = storyOrderedImageUrls[1], + title = resourceReference(R.string.swap_story_second_title), + subtitle = resourceReference(R.string.swap_story_second_subtitle), + ), + SwapStoriesUM.Content.Config( + imageUrl = storyOrderedImageUrls[2], + title = resourceReference(R.string.swap_story_third_title), + subtitle = resourceReference(R.string.swap_story_third_subtitle), + ), + SwapStoriesUM.Content.Config( + imageUrl = storyOrderedImageUrls[3], + title = resourceReference(R.string.swap_story_forth_title), + subtitle = resourceReference(R.string.swap_story_forth_subtitle), + ), + SwapStoriesUM.Content.Config( + imageUrl = storyOrderedImageUrls[4], + title = resourceReference(R.string.swap_story_fifth_title), + subtitle = resourceReference(R.string.swap_story_fifth_subtitle), + ), + ), + onClose = onStoriesClose, + ) + } +} \ No newline at end of file diff --git a/common/ui/src/main/java/com/tangem/common/ui/swapStoriesScreen/SwapStoriesUM.kt b/common/ui/src/main/java/com/tangem/common/ui/swapStoriesScreen/SwapStoriesUM.kt new file mode 100644 index 0000000000..24fc4dbae5 --- /dev/null +++ b/common/ui/src/main/java/com/tangem/common/ui/swapStoriesScreen/SwapStoriesUM.kt @@ -0,0 +1,26 @@ +package com.tangem.common.ui.swapStoriesScreen + +import com.tangem.core.ui.components.stories.model.StoriesContentConfig +import com.tangem.core.ui.components.stories.model.StoryConfig +import com.tangem.core.ui.extensions.TextReference +import kotlinx.collections.immutable.ImmutableList + +sealed class SwapStoriesUM { + + data object Empty : SwapStoriesUM() + + data class Content( + override val stories: ImmutableList, + override val onClose: () -> Unit, + ) : SwapStoriesUM(), StoriesContentConfig { + override val isRestartable: Boolean = false + + data class Config( + val imageUrl: String, + val title: TextReference, + val subtitle: TextReference, + ) : StoryConfig { + override val duration: Int = 2000 + } + } +} \ No newline at end of file diff --git a/features/stories/api/.gitignore b/features/stories/api/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/features/stories/api/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/features/stories/api/build.gradle.kts b/features/stories/api/build.gradle.kts new file mode 100644 index 0000000000..245c2c1b18 --- /dev/null +++ b/features/stories/api/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + id("kotlin-parcelize") + id("configuration") +} + +android { + namespace = "com.tangem.features.stories.api" +} + +dependencies { + /* Project - Core */ + implementation(projects.core.decompose) + implementation(projects.core.ui) + + /* Compose */ + implementation(deps.compose.runtime) + implementation(projects.common.routing) +} \ No newline at end of file diff --git a/features/stories/api/src/main/java/com/tangem/feature/stories/api/StoriesComponent.kt b/features/stories/api/src/main/java/com/tangem/feature/stories/api/StoriesComponent.kt new file mode 100644 index 0000000000..5311ebc07c --- /dev/null +++ b/features/stories/api/src/main/java/com/tangem/feature/stories/api/StoriesComponent.kt @@ -0,0 +1,15 @@ +package com.tangem.feature.stories.api + +import com.tangem.common.routing.AppRoute +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.decompose.ComposableContentComponent + +interface StoriesComponent : ComposableContentComponent { + + data class Params( + val storyId: String, + val nextScreen: AppRoute, + ) + + interface Factory : ComponentFactory +} \ No newline at end of file diff --git a/features/stories/impl/.gitignore b/features/stories/impl/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/features/stories/impl/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/features/stories/impl/build.gradle.kts b/features/stories/impl/build.gradle.kts new file mode 100644 index 0000000000..edc8449987 --- /dev/null +++ b/features/stories/impl/build.gradle.kts @@ -0,0 +1,43 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.kapt) + alias(deps.plugins.hilt.android) + id("configuration") +} + +android { + namespace = "com.tangem.features.stories.impl" +} + +dependencies { + /** Feature modules */ + implementation(projects.features.stories.api) + implementation(projects.features.swap.api) + + /** Domain modules */ + implementation(projects.domain.promo) + implementation(projects.domain.promo.models) + + /** Project - Common */ + implementation(projects.common.routing) + implementation(projects.common.ui) + + /** Project - Core */ + implementation(projects.core.configToggles) + implementation(projects.core.decompose) + implementation(projects.core.navigation) + implementation(projects.core.ui) + + /** AndroidX */ + implementation(deps.androidx.activity.compose) + implementation(deps.lifecycle.compose) + + /** Others */ + implementation(deps.timber) + implementation(deps.arrow.core) + + /** DI */ + implementation(deps.hilt.android) + kapt(deps.hilt.kapt) +} \ No newline at end of file diff --git a/features/stories/impl/src/main/java/com/tangem/feature/stories/impl/DefaultStoriesComponent.kt b/features/stories/impl/src/main/java/com/tangem/feature/stories/impl/DefaultStoriesComponent.kt new file mode 100644 index 0000000000..309d873b24 --- /dev/null +++ b/features/stories/impl/src/main/java/com/tangem/feature/stories/impl/DefaultStoriesComponent.kt @@ -0,0 +1,34 @@ +package com.tangem.feature.stories.impl + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.tangem.common.ui.swapStoriesScreen.SwapStoriesScreen +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.feature.stories.api.StoriesComponent +import com.tangem.feature.stories.impl.model.StoriesModel +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +@Stable +internal class DefaultStoriesComponent @AssistedInject constructor( + @Assisted context: AppComponentContext, + @Assisted params: StoriesComponent.Params, +) : StoriesComponent, AppComponentContext by context { + + private val model: StoriesModel = getOrCreateModel(params) + + @Composable + override fun Content(modifier: Modifier) { + val state = model.state.collectAsStateWithLifecycle() + SwapStoriesScreen(state.value) + } + + @AssistedFactory + interface Factory : StoriesComponent.Factory { + override fun create(context: AppComponentContext, params: StoriesComponent.Params): DefaultStoriesComponent + } +} \ No newline at end of file diff --git a/features/stories/impl/src/main/java/com/tangem/feature/stories/impl/di/StoriesComponentModule.kt b/features/stories/impl/src/main/java/com/tangem/feature/stories/impl/di/StoriesComponentModule.kt new file mode 100644 index 0000000000..9702daee20 --- /dev/null +++ b/features/stories/impl/src/main/java/com/tangem/feature/stories/impl/di/StoriesComponentModule.kt @@ -0,0 +1,18 @@ +package com.tangem.feature.stories.impl.di + +import com.tangem.feature.stories.api.StoriesComponent +import com.tangem.feature.stories.impl.DefaultStoriesComponent +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal interface StoriesComponentModule { + + @Binds + @Singleton + fun bindSwapStoriesComponentFactory(factory: DefaultStoriesComponent.Factory): StoriesComponent.Factory +} \ No newline at end of file diff --git a/features/stories/impl/src/main/java/com/tangem/feature/stories/impl/di/StoriesModelModule.kt b/features/stories/impl/src/main/java/com/tangem/feature/stories/impl/di/StoriesModelModule.kt new file mode 100644 index 0000000000..de6c20e886 --- /dev/null +++ b/features/stories/impl/src/main/java/com/tangem/feature/stories/impl/di/StoriesModelModule.kt @@ -0,0 +1,20 @@ +package com.tangem.feature.stories.impl.di + +import com.tangem.core.decompose.di.DecomposeComponent +import com.tangem.core.decompose.model.Model +import com.tangem.feature.stories.impl.model.StoriesModel +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap + +@Module +@InstallIn(DecomposeComponent::class) +internal interface StoriesModelModule { + + @Binds + @IntoMap + @ClassKey(StoriesModel::class) + fun bindStoriesModel(model: StoriesModel): Model +} \ No newline at end of file diff --git a/features/stories/impl/src/main/java/com/tangem/feature/stories/impl/model/StoriesModel.kt b/features/stories/impl/src/main/java/com/tangem/feature/stories/impl/model/StoriesModel.kt new file mode 100644 index 0000000000..088ab9a799 --- /dev/null +++ b/features/stories/impl/src/main/java/com/tangem/feature/stories/impl/model/StoriesModel.kt @@ -0,0 +1,64 @@ +package com.tangem.feature.stories.impl.model + +import com.tangem.common.ui.swapStoriesScreen.SwapStoriesFactory +import com.tangem.common.ui.swapStoriesScreen.SwapStoriesUM +import com.tangem.core.decompose.model.Model +import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.core.decompose.navigation.Router +import com.tangem.domain.promo.GetStoryContentUseCase +import com.tangem.domain.promo.ShouldShowSwapStoriesUseCase +import com.tangem.domain.promo.models.StoryContentIds +import com.tangem.feature.stories.api.StoriesComponent +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import timber.log.Timber +import javax.inject.Inject + +internal class StoriesModel @Inject constructor( + paramsContainer: ParamsContainer, + private val router: Router, + override val dispatchers: CoroutineDispatcherProvider, + private val getStoryContentUseCase: GetStoryContentUseCase, + private val shouldShowSwapStoriesUseCase: ShouldShowSwapStoriesUseCase, +) : Model() { + val state: StateFlow get() = _state + + private val params = paramsContainer.require() + private val _state: MutableStateFlow = MutableStateFlow( + value = SwapStoriesUM.Empty, + ) + + init { + initStories() + } + + private fun openScreen() { + modelScope.launch { + shouldShowSwapStoriesUseCase.neverToShow() + router.pop() + router.push(params.nextScreen) + } + } + + private fun initStories() { + modelScope.launch { + getStoryContentUseCase(params.storyId).fold( + ifLeft = { + Timber.e("Unable to load stories for ${StoryContentIds.STORY_FIRST_TIME_SWAP.id}") + openScreen() // Fallback to target screen + }, + ifRight = { swapStory -> + _state.update { + SwapStoriesFactory.createStoriesState( + swapStory = swapStory, + onStoriesClose = ::openScreen, + ) + } + }, + ) + } + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 42859fc826..627292c89e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -205,6 +205,9 @@ include(":features:markets:impl") include(":features:onramp:api") include(":features:onramp:impl") + +include(":features:stories:api") +include(":features:stories:impl") // endregion Feature modules // region Domain modules From 0cbf7b74e2b9053dd61da7419242327f08ee4a53 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 10 Feb 2025 11:08:04 +0500 Subject: [PATCH 3/4] Updated on 2026-08-14 --- .../tap/di/domain/TokensDomainModule.kt | 6 +++ .../swapStoriesScreen}/SwapStoriesScreen.kt | 12 +++--- domain/tokens/build.gradle.kts | 3 ++ .../tokens/GetCryptoCurrencyActionsUseCase.kt | 32 ++++++++++++--- .../domain/tokens/model/TokenActionsState.kt | 5 ++- .../feature/swap/models/SwapStateHolder.kt | 3 +- .../swap/models/SwapStoriesContentConfig.kt | 21 ---------- .../feature/swap/presentation/SwapFragment.kt | 2 +- .../tangem/feature/swap/ui/StateBuilder.kt | 39 ++----------------- features/tokendetails/impl/build.gradle.kts | 2 + .../tokendetails/TokenDetailsPreviewData.kt | 2 +- .../components/TokenDetailsActionButton.kt | 7 +++- .../TokenDetailsActionButtonsConverter.kt | 1 + .../TokenDetailsSkeletonStateConverter.kt | 4 +- 14 files changed, 64 insertions(+), 75 deletions(-) rename {features/swap/presentation/src/main/java/com/tangem/feature/swap/ui => common/ui/src/main/java/com/tangem/common/ui/swapStoriesScreen}/SwapStoriesScreen.kt (93%) delete mode 100644 features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStoriesContentConfig.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index aca1347d4e..2e266bd6b4 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -1,10 +1,12 @@ package com.tangem.tap.di.domain import com.tangem.domain.exchange.RampStateManager +import com.tangem.domain.promo.PromoRepository import com.tangem.domain.staking.repositories.StakingRepository import com.tangem.domain.tokens.* import com.tangem.domain.tokens.repository.* import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.features.swap.SwapFeatureToggles import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides @@ -227,6 +229,8 @@ internal object TokensDomainModule { quotesRepository: QuotesRepository, networksRepository: NetworksRepository, stakingRepository: StakingRepository, + promoRepository: PromoRepository, + swapFeatureToggles: SwapFeatureToggles, dispatchers: CoroutineDispatcherProvider, ): GetCryptoCurrencyActionsUseCase { return GetCryptoCurrencyActionsUseCase( @@ -236,6 +240,8 @@ internal object TokensDomainModule { quotesRepository = quotesRepository, networksRepository = networksRepository, stakingRepository = stakingRepository, + promoRepository = promoRepository, + swapFeatureToggles = swapFeatureToggles, dispatchers = dispatchers, ) } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapStoriesScreen.kt b/common/ui/src/main/java/com/tangem/common/ui/swapStoriesScreen/SwapStoriesScreen.kt similarity index 93% rename from features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapStoriesScreen.kt rename to common/ui/src/main/java/com/tangem/common/ui/swapStoriesScreen/SwapStoriesScreen.kt index 53f8089a14..ef8fd5f9b6 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapStoriesScreen.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/swapStoriesScreen/SwapStoriesScreen.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.swap.ui +package com.tangem.common.ui.swapStoriesScreen import android.content.res.Configuration import androidx.activity.compose.BackHandler @@ -33,15 +33,15 @@ import com.tangem.core.ui.res.LocalWindowSize import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview -import com.tangem.feature.swap.models.SwapStoriesContentConfig -import com.tangem.feature.swap.models.SwapStoryConfig import kotlinx.collections.immutable.persistentListOf private val SubtitleColor = Color(0xFF868692) private const val STORIES_RELATIVE_PADDING = 0.7 @Composable -internal fun SwapStoriesScreen(config: SwapStoriesContentConfig) { +fun SwapStoriesScreen(config: SwapStoriesUM) { + if (config !is SwapStoriesUM.Content) return + BackHandler(onBack = config.onClose) SystemBarsIconsDisposable(darkIcons = false) @@ -106,9 +106,9 @@ internal fun SwapStoriesScreen(config: SwapStoriesContentConfig) { private fun SwapStoriesScreen_Preview() { TangemThemePreview { SwapStoriesScreen( - SwapStoriesContentConfig( + SwapStoriesUM.Content( stories = persistentListOf( - SwapStoryConfig( + SwapStoriesUM.Content.Config( imageUrl = "https://devweb.tangem.com/images/stories/swap/image1.png", title = stringReference("Exchange With Us"), subtitle = stringReference( diff --git a/domain/tokens/build.gradle.kts b/domain/tokens/build.gradle.kts index 06966f21e2..0767da6383 100644 --- a/domain/tokens/build.gradle.kts +++ b/domain/tokens/build.gradle.kts @@ -25,11 +25,14 @@ dependencies { implementation(projects.domain.settings) implementation(projects.features.swap.domain.api) implementation(projects.features.swap.domain.models) + implementation(projects.domain.promo.models) + implementation(projects.domain.promo) /** Project - Api */ implementation(projects.features.send.api) implementation(projects.features.staking.api) implementation(projects.features.markets.api) + implementation(projects.features.swap.api) /** Project - Other */ implementation(projects.core.utils) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt index 1d4b2cc1d4..fb593d3249 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt @@ -2,6 +2,7 @@ package com.tangem.domain.tokens import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.exchange.RampStateManager +import com.tangem.domain.promo.PromoRepository import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.staking.repositories.StakingRepository import com.tangem.domain.tokens.model.* @@ -12,9 +13,9 @@ import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.transaction.models.AssetRequirementsCondition import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWallet +import com.tangem.features.swap.SwapFeatureToggles import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.isNullOrZero -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* import kotlinx.coroutines.withTimeoutOrNull @@ -31,10 +32,11 @@ class GetCryptoCurrencyActionsUseCase( private val quotesRepository: QuotesRepository, private val networksRepository: NetworksRepository, private val stakingRepository: StakingRepository, + private val promoRepository: PromoRepository, private val dispatchers: CoroutineDispatcherProvider, + private val swapFeatureToggles: SwapFeatureToggles, ) { - @OptIn(ExperimentalCoroutinesApi::class) suspend operator fun invoke( userWallet: UserWallet, cryptoCurrencyStatus: CryptoCurrencyStatus, @@ -62,13 +64,16 @@ class GetCryptoCurrencyActionsUseCase( includeQuotes = false, ) } - - val flow = networkFlow.mapLatest { maybeCoinStatus -> + val flow = combine( + flow = networkFlow, + flow2 = promoRepository.isReadyToShowSwapStories().distinctUntilChanged(), + ) { maybeCoinStatus, shouldShowSwapStories -> createTokenActionsState( userWallet = userWallet, coinStatus = maybeCoinStatus.getOrNull(), cryptoCurrencyStatus = cryptoCurrencyStatus, requirements = requirements, + shouldShowSwapStories = shouldShowSwapStories, ) } @@ -81,6 +86,7 @@ class GetCryptoCurrencyActionsUseCase( coinStatus: CryptoCurrencyStatus?, cryptoCurrencyStatus: CryptoCurrencyStatus, requirements: AssetRequirementsCondition?, + shouldShowSwapStories: Boolean, ): TokenActionsState { return TokenActionsState( walletId = userWallet.walletId, @@ -90,6 +96,7 @@ class GetCryptoCurrencyActionsUseCase( coinStatus = coinStatus, cryptoCurrencyStatus = cryptoCurrencyStatus, requirements = requirements, + shouldShowSwapStories = shouldShowSwapStories, ), ) } @@ -104,6 +111,7 @@ class GetCryptoCurrencyActionsUseCase( coinStatus: CryptoCurrencyStatus?, cryptoCurrencyStatus: CryptoCurrencyStatus, requirements: AssetRequirementsCondition?, + shouldShowSwapStories: Boolean, ): List { val cryptoCurrency = cryptoCurrencyStatus.currency if (cryptoCurrencyStatus.value is CryptoCurrencyStatus.MissedDerivation) { @@ -173,11 +181,18 @@ class GetCryptoCurrencyActionsUseCase( rampManager.availableForSwap(userWallet.walletId, cryptoCurrency) } ?: false if (isExchangeable && cryptoCurrencyStatus.value !is CryptoCurrencyStatus.NoQuote) { - activeList.add(TokenActionsState.ActionState.Swap(ScenarioUnavailabilityReason.None)) + val swapStoriesEnabled = swapFeatureToggles.isPromoStoriesEnabled + activeList.add( + TokenActionsState.ActionState.Swap( + unavailabilityReason = ScenarioUnavailabilityReason.None, + showBadge = shouldShowSwapStories && swapStoriesEnabled, + ), + ) } else { disabledList.add( TokenActionsState.ActionState.Swap( unavailabilityReason = ScenarioUnavailabilityReason.NotExchangeable(cryptoCurrency.name), + showBadge = false, ), ) } @@ -233,7 +248,12 @@ class GetCryptoCurrencyActionsUseCase( ) } actionsList.add(TokenActionsState.ActionState.Send(ScenarioUnavailabilityReason.Unreachable)) - actionsList.add(TokenActionsState.ActionState.Swap(ScenarioUnavailabilityReason.Unreachable)) + actionsList.add( + TokenActionsState.ActionState.Swap( + unavailabilityReason = ScenarioUnavailabilityReason.Unreachable, + showBadge = false, + ), + ) actionsList.add(TokenActionsState.ActionState.Sell(ScenarioUnavailabilityReason.Unreachable)) if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) { val scenario = getReceiveScenario(requirements) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenActionsState.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenActionsState.kt index 2531bc3ee2..dcfc255e79 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenActionsState.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenActionsState.kt @@ -26,7 +26,10 @@ data class TokenActionsState( val yield: Yield?, ) : ActionState() - data class Swap(override val unavailabilityReason: ScenarioUnavailabilityReason) : ActionState() + data class Swap( + override val unavailabilityReason: ScenarioUnavailabilityReason, + val showBadge: Boolean, + ) : ActionState() data class Send(override val unavailabilityReason: ScenarioUnavailabilityReason) : ActionState() diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt index 677d1d7e01..a041840318 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt @@ -4,6 +4,7 @@ import androidx.annotation.DrawableRes import androidx.compose.ui.text.input.TextFieldValue import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionState import com.tangem.common.ui.notifications.NotificationUM +import com.tangem.common.ui.swapStoriesScreen.SwapStoriesUM import com.tangem.core.ui.R import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.event.StateEvent @@ -34,7 +35,7 @@ internal data class SwapStateHolder( val successState: SwapSuccessStateHolder? = null, val selectTokenState: SwapSelectTokenStateHolder? = null, val bottomSheetConfig: TangemBottomSheetConfig? = null, - val storiesConfig: SwapStoriesContentConfig? = null, + val storiesConfig: SwapStoriesUM? = null, val swapButton: SwapButton, val shouldShowMaxAmount: Boolean, diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStoriesContentConfig.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStoriesContentConfig.kt deleted file mode 100644 index fd2b880962..0000000000 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStoriesContentConfig.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.feature.swap.models - -import com.tangem.core.ui.components.stories.model.StoriesContentConfig -import com.tangem.core.ui.components.stories.model.StoryConfig -import com.tangem.core.ui.extensions.TextReference -import kotlinx.collections.immutable.ImmutableList - -data class SwapStoriesContentConfig( - override val stories: ImmutableList, - override val onClose: () -> Unit, -) : StoriesContentConfig { - override val isRestartable: Boolean = false -} - -data class SwapStoryConfig( - val imageUrl: String, - val title: TextReference, - val subtitle: TextReference, -) : StoryConfig { - override val duration: Int = 2000 -} \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/presentation/SwapFragment.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/presentation/SwapFragment.kt index ff6ff1b947..58316f8c11 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/presentation/SwapFragment.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/presentation/SwapFragment.kt @@ -6,13 +6,13 @@ import androidx.compose.foundation.background import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.fragment.app.viewModels +import com.tangem.common.ui.swapStoriesScreen.SwapStoriesScreen import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.screen.ComposeFragment import com.tangem.feature.swap.router.SwapNavScreen import com.tangem.feature.swap.ui.SwapScreen import com.tangem.feature.swap.ui.SwapSelectTokenScreen -import com.tangem.feature.swap.ui.SwapStoriesScreen import com.tangem.feature.swap.ui.SwapSuccessScreen import com.tangem.feature.swap.viewmodels.SwapViewModel import dagger.hilt.android.AndroidEntryPoint diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index e667603319..3e7157f83e 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -5,6 +5,7 @@ import androidx.compose.ui.text.input.TextFieldValue import com.tangem.common.ui.alerts.models.AlertDemoModeUM import com.tangem.common.ui.bottomsheet.permission.state.* import com.tangem.common.ui.notifications.NotificationUM +import com.tangem.common.ui.swapStoriesScreen.SwapStoriesFactory import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.event.consumedEvent @@ -126,43 +127,11 @@ internal class StateBuilder( ) } - // WARNING! Be careful with indices. Temporary solution. - // Use all data from v1/stories api (image url, title, subtitle) - @Suppress("MagicNumber") fun createStoriesState(uiStateHolder: SwapStateHolder, swapStory: StoryContent): SwapStateHolder { - val storyOrderedImageUrls = swapStory.getImageUrls() - if (storyOrderedImageUrls.size != 5) return uiStateHolder - return uiStateHolder.copy( - storiesConfig = SwapStoriesContentConfig( - stories = persistentListOf( - SwapStoryConfig( - imageUrl = storyOrderedImageUrls[0], - title = resourceReference(R.string.swap_story_first_title), - subtitle = resourceReference(R.string.swap_story_first_subtitle), - ), - SwapStoryConfig( - imageUrl = storyOrderedImageUrls[1], - title = resourceReference(R.string.swap_story_second_title), - subtitle = resourceReference(R.string.swap_story_second_subtitle), - ), - SwapStoryConfig( - imageUrl = storyOrderedImageUrls[2], - title = resourceReference(R.string.swap_story_third_title), - subtitle = resourceReference(R.string.swap_story_third_subtitle), - ), - SwapStoryConfig( - imageUrl = storyOrderedImageUrls[3], - title = resourceReference(R.string.swap_story_forth_title), - subtitle = resourceReference(R.string.swap_story_forth_subtitle), - ), - SwapStoryConfig( - imageUrl = storyOrderedImageUrls[4], - title = resourceReference(R.string.swap_story_fifth_title), - subtitle = resourceReference(R.string.swap_story_fifth_subtitle), - ), - ), - onClose = actions.onStoriesClose, + storiesConfig = SwapStoriesFactory.createStoriesState( + swapStory = swapStory, + onStoriesClose = actions.onStoriesClose, ), ) } diff --git a/features/tokendetails/impl/build.gradle.kts b/features/tokendetails/impl/build.gradle.kts index adb8f4302f..4a38dc8f9b 100644 --- a/features/tokendetails/impl/build.gradle.kts +++ b/features/tokendetails/impl/build.gradle.kts @@ -85,6 +85,7 @@ dependencies { implementation(projects.domain.onramp) implementation(projects.domain.onramp.models) implementation(projects.domain.promo) + implementation(projects.domain.promo.models) /** Temp dependency to swap domain */ implementation(projects.features.swap.domain) @@ -97,5 +98,6 @@ dependencies { implementation(projects.features.staking.api) implementation(projects.features.markets.api) implementation(projects.features.onramp.api) + implementation(projects.features.swap.api) } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt index 64201c4872..01d0aec7ba 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt @@ -100,7 +100,7 @@ internal object TokenDetailsPreviewData { TokenDetailsActionButton.Buy(dimContent = false, onClick = {}), TokenDetailsActionButton.Send(dimContent = false, onClick = {}), TokenDetailsActionButton.Receive(onClick = {}, onLongClick = null), - TokenDetailsActionButton.Swap(dimContent = false, onClick = {}), + TokenDetailsActionButton.Swap(dimContent = false, onClick = {}, showBadge = true), ) private val balanceSegmentedButtonConfig = persistentListOf( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsActionButton.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsActionButton.kt index c5b6078527..b3c3f03e68 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsActionButton.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsActionButton.kt @@ -83,12 +83,17 @@ internal sealed class TokenDetailsActionButton(val config: ActionButtonConfig) { * @property dimContent determines whether the button content will be dimmed * @property onClick lambda be invoked when Swap button is clicked */ - data class Swap(val dimContent: Boolean, override val onClick: () -> Unit) : TokenDetailsActionButton( + data class Swap( + val dimContent: Boolean, + val showBadge: Boolean, + override val onClick: () -> Unit, + ) : TokenDetailsActionButton( config = ActionButtonConfig( text = TextReference.Res(id = R.string.swapping_swap_action), iconResId = R.drawable.ic_exchange_vertical_24, onClick = onClick, dimContent = dimContent, + showBadge = showBadge, ), ) } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsActionButtonsConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsActionButtonsConverter.kt index dbf8a99f3c..c68c1e3be2 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsActionButtonsConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsActionButtonsConverter.kt @@ -54,6 +54,7 @@ internal class TokenDetailsActionButtonsConverter( TokenDetailsActionButton.Swap( dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None, onClick = { clickIntents.onSwapClick(action.unavailabilityReason) }, + showBadge = action.showBadge, ) } else -> { diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt index 3a5d25730d..44798192e1 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt @@ -1,12 +1,12 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory import arrow.core.getOrElse +import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.networkIconResId import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig import com.tangem.core.ui.res.TangemTheme import com.tangem.domain.card.NetworkHasDerivationUseCase import com.tangem.domain.staking.GetStakingIntegrationIdUseCase @@ -122,7 +122,7 @@ internal class TokenDetailsSkeletonStateConverter( TokenDetailsActionButton.Send(dimContent = false, onClick = {}), TokenDetailsActionButton.Receive(onClick = {}, onLongClick = null), TokenDetailsActionButton.Sell(dimContent = false, onClick = {}), - TokenDetailsActionButton.Swap(dimContent = false, onClick = {}), + TokenDetailsActionButton.Swap(dimContent = false, onClick = {}, showBadge = false), ) } From 8565d7463840cb97cc3e00dbbea7f8ecc3be9832 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 10 Feb 2025 11:08:23 +0500 Subject: [PATCH 4/4] Updated on 2026-08-14 --- features/wallet/impl/build.gradle.kts | 1 + .../implementors/MultiWalletContentLoader.kt | 11 ++++++ .../MultiWalletContentLoaderFactory.kt | 6 ++++ .../SingleWalletWithTokenContentLoader.kt | 11 ++++++ ...ngleWalletWithTokenContentLoaderFactory.kt | 6 ++++ .../wallet/state/model/WalletManageButton.kt | 2 ++ ...MultiWalletActionButtonBadgeTransformer.kt | 19 ++++++++++ .../state/utils/MultiWalletActionsExt.kt | 11 ++++++ .../MultiWalletActionButtonsSubscriber.kt | 30 ++++++++++++++++ .../WalletCurrencyActionsClickIntents.kt | 35 ++++++++++++++++--- 10 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UpdateMultiWalletActionButtonBadgeTransformer.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletActionButtonsSubscriber.kt diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index 0681996220..3749a2f4a1 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -105,6 +105,7 @@ dependencies { implementation(projects.features.markets.api) implementation(projects.features.onramp.api) implementation(projects.features.onboardingV2.api) + implementation(projects.features.swap.api) /** Common modules */ implementation(projects.common) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt index 97031f4f76..87438f6260 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt @@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors import com.tangem.core.deeplink.DeepLinksRegistry import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.promo.ShouldShowSwapStoriesUseCase import com.tangem.domain.tokens.ApplyTokenListSortingUseCase import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase import com.tangem.domain.wallets.models.UserWallet @@ -12,10 +13,12 @@ import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarnin import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletActionButtonsSubscriber import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletTokenListSubscriber import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletWarningsSubscriber import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents +import com.tangem.features.swap.SwapFeatureToggles @Suppress("LongParameterList") internal class MultiWalletContentLoader( @@ -31,6 +34,8 @@ internal class MultiWalletContentLoader( private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase, private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory, private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase, + private val shouldShowSwapStoriesUseCase: ShouldShowSwapStoriesUseCase, + private val swapFeatureToggles: SwapFeatureToggles, private val deepLinksRegistry: DeepLinksRegistry, ) : WalletContentLoader(id = userWallet.walletId) { @@ -56,6 +61,12 @@ internal class MultiWalletContentLoader( walletWarningsAnalyticsSender = walletWarningsAnalyticsSender, walletWarningsSingleEventSender = walletWarningsSingleEventSender, ), + MultiWalletActionButtonsSubscriber( + userWallet = userWallet, + stateHolder = stateHolder, + shouldShowSwapStoriesUseCase = shouldShowSwapStoriesUseCase, + swapFeatureToggles = swapFeatureToggles, + ), ) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt index 8f3777af4d..3107bd8568 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt @@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors import com.tangem.core.deeplink.DeepLinksRegistry import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.promo.ShouldShowSwapStoriesUseCase import com.tangem.domain.tokens.ApplyTokenListSortingUseCase import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase import com.tangem.domain.wallets.models.UserWallet @@ -13,6 +14,7 @@ import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenList import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents +import com.tangem.features.swap.SwapFeatureToggles import dagger.hilt.android.scopes.ViewModelScoped import javax.inject.Inject @@ -29,6 +31,8 @@ internal class MultiWalletContentLoaderFactory @Inject constructor( private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender, private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase, + private val shouldShowSwapStoriesUseCase: ShouldShowSwapStoriesUseCase, + private val swapFeatureToggles: SwapFeatureToggles, private val deepLinksRegistry: DeepLinksRegistry, ) { @@ -46,6 +50,8 @@ internal class MultiWalletContentLoaderFactory @Inject constructor( walletWarningsSingleEventSender = walletWarningsSingleEventSender, applyTokenListSortingUseCase = applyTokenListSortingUseCase, runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase, + shouldShowSwapStoriesUseCase = shouldShowSwapStoriesUseCase, + swapFeatureToggles = swapFeatureToggles, deepLinksRegistry = deepLinksRegistry, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoader.kt index 6e494de5b7..f8c8dc9ea5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoader.kt @@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors import com.tangem.core.deeplink.DeepLinksRegistry import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.promo.ShouldShowSwapStoriesUseCase import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender @@ -11,10 +12,12 @@ import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarnin import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletActionButtonsSubscriber import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletWarningsSubscriber import com.tangem.feature.wallet.presentation.wallet.subscribers.SingleWalletWithTokenListSubscriber import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents +import com.tangem.features.swap.SwapFeatureToggles @Suppress("LongParameterList") internal class SingleWalletWithTokenContentLoader( @@ -29,6 +32,8 @@ internal class SingleWalletWithTokenContentLoader( private val tokenListStore: MultiWalletTokenListStore, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase, + private val shouldShowSwapStoriesUseCase: ShouldShowSwapStoriesUseCase, + private val swapFeatureToggles: SwapFeatureToggles, private val deepLinksRegistry: DeepLinksRegistry, ) : WalletContentLoader(id = userWallet.walletId) { @@ -53,6 +58,12 @@ internal class SingleWalletWithTokenContentLoader( walletWarningsAnalyticsSender = walletWarningsAnalyticsSender, walletWarningsSingleEventSender = walletWarningsSingleEventSender, ), + MultiWalletActionButtonsSubscriber( + userWallet = userWallet, + stateHolder = stateHolder, + shouldShowSwapStoriesUseCase = shouldShowSwapStoriesUseCase, + swapFeatureToggles = swapFeatureToggles, + ), ) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoaderFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoaderFactory.kt index fb2790cc51..8d16529907 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoaderFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoaderFactory.kt @@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors import com.tangem.core.deeplink.DeepLinksRegistry import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.promo.ShouldShowSwapStoriesUseCase import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender @@ -12,6 +13,7 @@ import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenList import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents +import com.tangem.features.swap.SwapFeatureToggles import javax.inject.Inject // TODO: Refactor @@ -26,6 +28,8 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor( private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender, private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase, + private val shouldShowSwapStoriesUseCase: ShouldShowSwapStoriesUseCase, + private val swapFeatureToggles: SwapFeatureToggles, private val deepLinksRegistry: DeepLinksRegistry, ) { @@ -42,6 +46,8 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor( walletWarningsAnalyticsSender = walletWarningsAnalyticsSender, walletWarningsSingleEventSender = walletWarningsSingleEventSender, runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase, + shouldShowSwapStoriesUseCase = shouldShowSwapStoriesUseCase, + swapFeatureToggles = swapFeatureToggles, deepLinksRegistry = deepLinksRegistry, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletManageButton.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletManageButton.kt index 515041aec5..af52d510ec 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletManageButton.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletManageButton.kt @@ -143,6 +143,7 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) { override val dimContent: Boolean, override val onClick: () -> Unit, val isInProgress: Boolean = false, + val showBadge: Boolean = false, ) : WalletManageButton( config = ActionButtonConfig( text = TextReference.Res(id = R.string.swapping_swap_action), @@ -151,6 +152,7 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) { enabled = enabled, dimContent = dimContent, isInProgress = isInProgress, + showBadge = showBadge, ), ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UpdateMultiWalletActionButtonBadgeTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UpdateMultiWalletActionButtonBadgeTransformer.kt new file mode 100644 index 0000000000..dc39424862 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UpdateMultiWalletActionButtonBadgeTransformer.kt @@ -0,0 +1,19 @@ +package com.tangem.feature.wallet.presentation.wallet.state.transformers + +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.utils.showSwapBadge + +internal class UpdateMultiWalletActionButtonBadgeTransformer( + private val showSwapBadge: Boolean, + userWalletId: UserWalletId, +) : WalletStateTransformer(userWalletId) { + override fun transform(prevState: WalletState): WalletState { + return when (prevState) { + is WalletState.MultiCurrency.Content -> { + prevState.copy(buttons = prevState.showSwapBadge(showBadge = showSwapBadge)) + } + else -> prevState + } + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/MultiWalletActionsExt.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/MultiWalletActionsExt.kt index e109933f15..b89808ca72 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/MultiWalletActionsExt.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/MultiWalletActionsExt.kt @@ -24,4 +24,15 @@ private fun WalletState.MultiCurrency.Content.changeAvailability(enabled: Boolea } } .toPersistentList() +} + +internal fun WalletState.MultiCurrency.Content.showSwapBadge(showBadge: Boolean): PersistentList { + return buttons + .map { action -> + when (action) { + is WalletManageButton.Swap -> action.copy(showBadge = showBadge) + else -> action + } + } + .toPersistentList() } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletActionButtonsSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletActionButtonsSubscriber.kt new file mode 100644 index 0000000000..9c44ec3c2e --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletActionButtonsSubscriber.kt @@ -0,0 +1,30 @@ +package com.tangem.feature.wallet.presentation.wallet.subscribers + +import com.tangem.domain.promo.ShouldShowSwapStoriesUseCase +import com.tangem.domain.wallets.models.UserWallet +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.transformers.UpdateMultiWalletActionButtonBadgeTransformer +import com.tangem.features.swap.SwapFeatureToggles +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +internal class MultiWalletActionButtonsSubscriber( + private val userWallet: UserWallet, + private val stateHolder: WalletStateController, + private val shouldShowSwapStoriesUseCase: ShouldShowSwapStoriesUseCase, + private val swapFeatureToggles: SwapFeatureToggles, +) : WalletSubscriber() { + override fun create(coroutineScope: CoroutineScope): Flow<*> = shouldShowSwapStoriesUseCase() + .distinctUntilChanged() + .map { showSwapBadge -> + val showBadge = swapFeatureToggles.isPromoStoriesEnabled && showSwapBadge + stateHolder.update( + UpdateMultiWalletActionButtonBadgeTransformer( + userWalletId = userWallet.walletId, + showSwapBadge = showBadge, + ), + ) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt index db4d42f963..1e4152a1a0 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt @@ -28,6 +28,8 @@ import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.exchange.RampStateManager import com.tangem.domain.markets.TokenMarketParams import com.tangem.domain.onramp.model.OnrampSource +import com.tangem.domain.promo.ShouldShowSwapStoriesUseCase +import com.tangem.domain.promo.models.StoryContentIds import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase @@ -51,6 +53,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensLis import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender import com.tangem.features.onramp.OnrampFeatureToggles +import com.tangem.features.swap.SwapFeatureToggles import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.scopes.ViewModelScoped import kotlinx.collections.immutable.toImmutableList @@ -110,6 +113,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( private val removeCurrencyUseCase: RemoveCurrencyUseCase, private val getExploreUrlUseCase: GetExploreUrlUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, + private val shouldShowSwapStoriesUseCase: ShouldShowSwapStoriesUseCase, private val analyticsEventHandler: AnalyticsEventHandler, private val dispatchers: CoroutineDispatcherProvider, private val reduxStateHolder: ReduxStateHolder, @@ -119,6 +123,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( private val appRouter: AppRouter, private val rampStateManager: RampStateManager, private val onrampFeatureToggles: OnrampFeatureToggles, + private val swapFeatureToggles: SwapFeatureToggles, ) : BaseWalletClickIntents(), WalletCurrencyActionsClickIntents { override fun onSendClick( @@ -430,11 +435,16 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( return } - onMultiWalletActionClick( - statusFlow = rampStateManager.getExpressInitializationStatus(userWalletId), - route = AppRoute.SwapCrypto(userWalletId = userWalletId), - eventCreator = MainScreenAnalyticsEvent::ButtonSwap, - ) + viewModelScope.launch { + val swapRoute = getSwapRoute( + AppRoute.SwapCrypto(userWalletId = userWalletId), + ) + onMultiWalletActionClick( + statusFlow = rampStateManager.getExpressInitializationStatus(userWalletId), + route = swapRoute, + eventCreator = MainScreenAnalyticsEvent::ButtonSwap, + ) + } } override fun onMultiWalletBuyClick(userWalletId: UserWalletId) { @@ -597,4 +607,19 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( event = WalletEvent.ShowAlert(state = WalletAlertState.ProvidersStillLoading), ) } + + private suspend fun getSwapRoute(targetRoute: AppRoute): AppRoute { + val isSwapStoriesEnabled = swapFeatureToggles.isPromoStoriesEnabled + val shouldShowSwapStories = shouldShowSwapStoriesUseCase.invokeSync() + val showSwapStories = shouldShowSwapStories && isSwapStoriesEnabled + + return if (showSwapStories) { + AppRoute.Stories( + storyId = StoryContentIds.STORY_FIRST_TIME_SWAP.id, + nextScreen = targetRoute, + ) + } else { + targetRoute + } + } } \ No newline at end of file