diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 048e2f771d..7c5c71355a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -275,7 +275,7 @@ dependencies { implementation(projects.features.tokenRecieve.api) implementation(projects.features.tokenRecieve.impl) implementation(projects.features.yieldSupply.api) - implementation(projects.features.yieldSupply.api) + implementation(projects.features.yieldSupply.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 6035f506dc..fb0bfb3588 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 @@ -34,9 +34,11 @@ import com.tangem.features.staking.api.StakingComponent import com.tangem.features.swap.SwapComponent import com.tangem.features.swap.v2.api.SendWithSwapComponent import com.tangem.features.tangempay.components.TangemPayDetailsComponent +import com.tangem.features.tangempay.components.TangemPayOnboardingComponent import com.tangem.features.tokendetails.TokenDetailsComponent import com.tangem.features.wallet.WalletEntryComponent import com.tangem.features.walletconnect.components.WalletConnectEntryComponent +import com.tangem.features.yield.supply.api.YieldSupplyPromoComponent import com.tangem.tap.features.details.ui.appcurrency.api.AppCurrencySelectorComponent import com.tangem.tap.features.details.ui.appsettings.api.AppSettingsComponent import com.tangem.tap.features.details.ui.cardsettings.api.CardSettingsComponent @@ -45,7 +47,6 @@ import com.tangem.tap.features.details.ui.resetcard.api.ResetCardComponent import com.tangem.tap.features.details.ui.securitymode.api.SecurityModeComponent import com.tangem.tap.features.welcome.component.WelcomeComponent import com.tangem.tap.routing.component.RoutingComponent.Child -import com.tangem.features.tangempay.components.TangemPayOnboardingComponent import dagger.hilt.android.scopes.ActivityScoped import javax.inject.Inject import com.tangem.features.walletconnect.components.WalletConnectEntryComponent as RedesignedWalletConnectComponent @@ -105,6 +106,7 @@ internal class ChildFactory @Inject constructor( private val sendEntryPointComponentFactory: SendEntryPointComponent.Factory, private val tangemPayDetailsComponentFactory: TangemPayDetailsComponent.Factory, private val tangemPayOnboardingComponentFactory: TangemPayOnboardingComponent.Factory, + private val yieldSupplyPromoComponentFactory: YieldSupplyPromoComponent.Factory, private val hotWalletFeatureToggles: HotWalletFeatureToggles, ) { @@ -600,6 +602,13 @@ internal class ChildFactory @Inject constructor( componentFactory = tangemPayOnboardingComponentFactory, ) } + is AppRoute.YieldSupplyPromo -> { + createComponentChild( + context = context, + params = YieldSupplyPromoComponent.Params(route.userWalletId, route.cryptoCurrency), + componentFactory = yieldSupplyPromoComponentFactory, + ) + } } } } \ No newline at end of file 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 3ca2c4c565..448f369ac0 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 @@ -382,4 +382,10 @@ sealed class AppRoute(val path: String) : Route { data class TangemPayOnboarding( val deeplink: String, ) : AppRoute(path = "/tangem_pay_onboarding/$deeplink") + + @Serializable + data class YieldSupplyPromo( + val userWalletId: UserWalletId, + val cryptoCurrency: CryptoCurrency, + ) : AppRoute(path = "/yield_supply_promo/${userWalletId.stringValue}/${cryptoCurrency.symbol}") } \ No newline at end of file diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index 0ec41dbaeb..50f599d85a 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -1657,7 +1657,7 @@ Aaveは世界中で何百万人もの人々に信頼されています。総貸付額は104億ドルです。 分散型・自己管理型 サービスを使用することにより、プロバイダーに同意したものとみなされます - 年間%s%の収益 + 年間%s%%の収益 Aave • 変動金利 Aave 平均%s diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 4db0f5063e..fb4b2b7a3b 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -200,6 +200,7 @@ You have not given access to your camera, please adjust your privacy settings Cancel Change + Choose account Choose action Choose network Choose token @@ -1266,6 +1267,14 @@ You receive Choose token not available + Get card + Add to your wallet and pay with your phone anywhere. + Apple Pay & Google Pay + Use your USDC balance to pay for everyday purchases seamlessly. + Everyday purchases + Your card details are protected — full control in the app. + Built-in security + Get your free Crypto Card\nin minutes This is my wallet Balances hidden Balances shown @@ -1414,14 +1423,6 @@ Unlock Scan your card to unlock access Needed unlock - Get your free Crypto Card\nin minutes - Built-in security - Your card details are protected — full control in the app. - Everyday purchases - Use your USDC balance to pay for everyday purchases seamlessly. - Apple Pay & Google Pay - Add to your wallet and pay with your phone anywhere. - Get card Blockchain is unreachable. Try later Scan card or ring This wallet has already been activated earlier.\nIf it was not done by you, please contact support.\nTangem never sells wallets along with pre-generated access codes. @@ -1732,8 +1733,8 @@ How it works? Aave is trusted by millions worldwide. Total lended value is $10.4B. Decentralized and self-custodial - By using service, you agree with provider - Earn %s% yearly + By using service, you agree with provider %1$s and %2$s + Earn %s%% yearly Aave • Variable Interest Rate Aave Avg %s diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/label/Label.kt b/core/ui/src/main/java/com/tangem/core/ui/components/label/Label.kt index 7e95a24776..86dacb20d6 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/label/Label.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/label/Label.kt @@ -2,19 +2,22 @@ package com.tangem.core.ui.components.label import android.content.res.Configuration import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateColorAsState import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.tangem.core.ui.R import com.tangem.core.ui.components.label.entity.LabelStyle import com.tangem.core.ui.components.label.entity.LabelUM import com.tangem.core.ui.extensions.TextReference @@ -48,8 +51,18 @@ fun Label(state: LabelUM, modifier: Modifier = Modifier) { }, ) + val iconColor by animateColorAsState( + targetValue = when (state.style) { + LabelStyle.ACCENT -> TangemTheme.colors.icon.accent + LabelStyle.REGULAR -> TangemTheme.colors.icon.informative + LabelStyle.WARNING -> TangemTheme.colors.icon.warning + }, + ) + AnimatedContent(targetState = state.text) { text -> - Box( + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), modifier = modifier .padding(horizontal = 4.dp) .background( @@ -63,6 +76,15 @@ fun Label(state: LabelUM, modifier: Modifier = Modifier) { style = TangemTheme.typography.caption1, color = textColor, ) + AnimatedVisibility(state.icon != null) { + val wrappedIcon = remember(this) { requireNotNull(state.icon) } + Icon( + imageVector = ImageVector.vectorResource(wrappedIcon), + tint = iconColor, + contentDescription = null, + modifier = Modifier.size(16.dp), + ) + } } } } @@ -73,6 +95,7 @@ fun Label(state: LabelUM, modifier: Modifier = Modifier) { private fun LabelPreview() { TangemThemePreview { Column( + verticalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.padding(16.dp), ) { Label( @@ -81,24 +104,39 @@ private fun LabelPreview() { style = LabelStyle.REGULAR, ), ) - - Spacer(modifier = Modifier.height(8.dp)) - Label( state = LabelUM( text = TextReference.Str("Accent Label"), style = LabelStyle.ACCENT, ), ) - - Spacer(modifier = Modifier.height(8.dp)) - Label( state = LabelUM( text = TextReference.Str("Warning Label"), style = LabelStyle.WARNING, ), ) + Label( + state = LabelUM( + text = TextReference.Str("Regular Label"), + style = LabelStyle.REGULAR, + icon = R.drawable.ic_information_24, + ), + ) + Label( + state = LabelUM( + text = TextReference.Str("Accent Label"), + style = LabelStyle.ACCENT, + icon = R.drawable.ic_information_24, + ), + ) + Label( + state = LabelUM( + text = TextReference.Str("Warning Label"), + style = LabelStyle.WARNING, + icon = R.drawable.ic_information_24, + ), + ) } } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/label/entity/LabelUM.kt b/core/ui/src/main/java/com/tangem/core/ui/components/label/entity/LabelUM.kt index 70b134bfe8..1be654c994 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/label/entity/LabelUM.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/label/entity/LabelUM.kt @@ -1,10 +1,13 @@ package com.tangem.core.ui.components.label.entity +import androidx.annotation.DrawableRes import com.tangem.core.ui.extensions.TextReference data class LabelUM( val text: TextReference, val style: LabelStyle, + @DrawableRes val icon: Int? = null, + val onIconClick: (() -> Unit)? = null, ) enum class LabelStyle { diff --git a/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableBottomSheetComponent.kt b/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableBottomSheetComponent.kt index 2e3d83710f..6128a71c4a 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableBottomSheetComponent.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableBottomSheetComponent.kt @@ -10,4 +10,13 @@ interface ComposableBottomSheetComponent { @Composable fun BottomSheet() +} + +fun getEmptyComposableBottomSheetComponent() = EmptyComposableBottomSheetComponent + +object EmptyComposableBottomSheetComponent : ComposableBottomSheetComponent { + override fun dismiss() {} + + @Composable + override fun BottomSheet() {} } \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/ic_analytics_up_24.xml b/core/ui/src/main/res/drawable/ic_analytics_up_24.xml new file mode 100644 index 0000000000..be13c86516 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_analytics_up_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_flash_new_24.xml b/core/ui/src/main/res/drawable/ic_flash_new_24.xml new file mode 100644 index 0000000000..71ad60eda8 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_flash_new_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_repeat_24.xml b/core/ui/src/main/res/drawable/ic_repeat_24.xml new file mode 100644 index 0000000000..deb5797a0b --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_repeat_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_security_check_24.xml b/core/ui/src/main/res/drawable/ic_security_check_24.xml new file mode 100644 index 0000000000..8a64f5924c --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_security_check_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/features/yield-supply/api/src/main/java/com/tangem/features/yield/supply/api/YieldSupplyPromoComponent.kt b/features/yield-supply/api/src/main/java/com/tangem/features/yield/supply/api/YieldSupplyPromoComponent.kt new file mode 100644 index 0000000000..0516efb037 --- /dev/null +++ b/features/yield-supply/api/src/main/java/com/tangem/features/yield/supply/api/YieldSupplyPromoComponent.kt @@ -0,0 +1,16 @@ +package com.tangem.features.yield.supply.api + +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.wallet.UserWalletId + +interface YieldSupplyPromoComponent : ComposableContentComponent { + + data class Params( + val userWalletId: UserWalletId, + val currency: CryptoCurrency, + ) + + interface Factory : ComponentFactory +} \ No newline at end of file diff --git a/features/yield-supply/impl/build.gradle.kts b/features/yield-supply/impl/build.gradle.kts index 14dbb06d31..95021510f8 100644 --- a/features/yield-supply/impl/build.gradle.kts +++ b/features/yield-supply/impl/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation(projects.core.configToggles) implementation(projects.core.decompose) implementation(projects.core.ui) + implementation(projects.core.navigation) /** Common */ implementation(projects.common.ui) diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/DefaultYieldSupplyPromoComponent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/DefaultYieldSupplyPromoComponent.kt new file mode 100644 index 0000000000..7e9414c4a1 --- /dev/null +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/DefaultYieldSupplyPromoComponent.kt @@ -0,0 +1,56 @@ +package com.tangem.features.yield.supply.impl.promo + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import com.arkivanov.decompose.extensions.compose.subscribeAsState +import com.arkivanov.decompose.router.slot.childSlot +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.ui.decompose.ComposableBottomSheetComponent +import com.tangem.core.ui.decompose.getEmptyComposableBottomSheetComponent +import com.tangem.features.yield.supply.api.YieldSupplyPromoComponent +import com.tangem.features.yield.supply.impl.promo.model.YieldSupplyPromoModel +import com.tangem.features.yield.supply.impl.promo.ui.YieldSupplyPromoContent +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +internal class DefaultYieldSupplyPromoComponent @AssistedInject constructor( + @Assisted private val appComponentContext: AppComponentContext, + @Assisted private val params: YieldSupplyPromoComponent.Params, +) : YieldSupplyPromoComponent, AppComponentContext by appComponentContext { + + private val model: YieldSupplyPromoModel = getOrCreateModel(params = params) + + private val bottomSheetSlot = childSlot( + source = model.bottomSheetNavigation, + serializer = null, + handleBackButton = false, + childFactory = { _, _ -> bottomSheetChild() }, + ) + + @Composable + override fun Content(modifier: Modifier) { + val state = model.uiState + val bottomSheet by bottomSheetSlot.subscribeAsState() + + YieldSupplyPromoContent( + yieldSupplyPromoUM = state, + clickIntents = model, + modifier = modifier, + ) + + bottomSheet.child?.instance?.BottomSheet() + } + + private fun bottomSheetChild(): ComposableBottomSheetComponent = getEmptyComposableBottomSheetComponent() + + @AssistedFactory + interface Factory : YieldSupplyPromoComponent.Factory { + override fun create( + context: AppComponentContext, + params: YieldSupplyPromoComponent.Params, + ): DefaultYieldSupplyPromoComponent + } +} \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/YieldSupplyPromoConfig.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/YieldSupplyPromoConfig.kt new file mode 100644 index 0000000000..7a87ea9946 --- /dev/null +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/YieldSupplyPromoConfig.kt @@ -0,0 +1,6 @@ +package com.tangem.features.yield.supply.impl.promo + +internal enum class YieldSupplyPromoConfig { + Apy, + Action, +} \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/di/YieldSupplyPromoBindsModule.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/di/YieldSupplyPromoBindsModule.kt new file mode 100644 index 0000000000..95e0a79cc6 --- /dev/null +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/di/YieldSupplyPromoBindsModule.kt @@ -0,0 +1,34 @@ +package com.tangem.features.yield.supply.impl.promo.di + +import com.tangem.core.decompose.di.ModelComponent +import com.tangem.core.decompose.model.Model +import com.tangem.features.yield.supply.impl.promo.DefaultYieldSupplyPromoComponent +import com.tangem.features.yield.supply.impl.promo.model.YieldSupplyPromoModel +import com.tangem.features.yield.supply.api.YieldSupplyPromoComponent +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal interface YieldSupplyPromoBindsModule { + @Binds + @Singleton + fun provideYieldSupplyPromoComponentFactory( + impl: DefaultYieldSupplyPromoComponent.Factory, + ): YieldSupplyPromoComponent.Factory +} + +@Module +@InstallIn(ModelComponent::class) +internal interface YieldSupplyPromoModelModule { + + @Binds + @IntoMap + @ClassKey(YieldSupplyPromoModel::class) + fun provideYieldSupplyPromoModel(impl: YieldSupplyPromoModel): Model +} \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/entity/YieldSupplyPromoUM.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/entity/YieldSupplyPromoUM.kt new file mode 100644 index 0000000000..51eefdad41 --- /dev/null +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/entity/YieldSupplyPromoUM.kt @@ -0,0 +1,9 @@ +package com.tangem.features.yield.supply.impl.promo.entity + +import com.tangem.core.ui.extensions.TextReference + +data class YieldSupplyPromoUM( + val tosLink: String, + val policyLink: String, + val title: TextReference, +) \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/model/YieldSupplyPromoClickIntents.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/model/YieldSupplyPromoClickIntents.kt new file mode 100644 index 0000000000..0bf70fa048 --- /dev/null +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/model/YieldSupplyPromoClickIntents.kt @@ -0,0 +1,14 @@ +package com.tangem.features.yield.supply.impl.promo.model + +internal interface YieldSupplyPromoClickIntents { + + fun onBackClick() + + fun onApyInfoClick() + + fun onHowItWorksClick() + + fun onStartEarningClick() + + fun onUrlClick(url: String) +} \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/model/YieldSupplyPromoModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/model/YieldSupplyPromoModel.kt new file mode 100644 index 0000000000..9dbc5ecdd9 --- /dev/null +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/model/YieldSupplyPromoModel.kt @@ -0,0 +1,51 @@ +package com.tangem.features.yield.supply.impl.promo.model + +import com.arkivanov.decompose.router.slot.SlotNavigation +import com.arkivanov.decompose.router.slot.activate +import com.tangem.common.routing.AppRouter +import com.tangem.core.decompose.di.ModelScoped +import com.tangem.core.decompose.model.Model +import com.tangem.core.navigation.url.UrlOpener +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.features.yield.supply.impl.R +import com.tangem.features.yield.supply.impl.promo.YieldSupplyPromoConfig +import com.tangem.features.yield.supply.impl.promo.entity.YieldSupplyPromoUM +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import javax.inject.Inject + +@ModelScoped +internal class YieldSupplyPromoModel @Inject constructor( + override val dispatchers: CoroutineDispatcherProvider, + private val urlOpener: UrlOpener, + private val appRouter: AppRouter, +) : Model(), YieldSupplyPromoClickIntents { + + val uiState: YieldSupplyPromoUM = YieldSupplyPromoUM( + tosLink = "https://tangem.com/terms-of-service/", // TODO replace with real link + policyLink = "https://tangem.com/privacy-policy/", // TODO replace with real link + title = resourceReference(R.string.yield_module_promo_screen_title, wrappedList("5.3")), + ) + + val bottomSheetNavigation: SlotNavigation = SlotNavigation() + + override fun onBackClick() { + appRouter.pop() + } + + override fun onApyInfoClick() { + bottomSheetNavigation.activate(YieldSupplyPromoConfig.Apy) + } + + override fun onUrlClick(url: String) { + urlOpener.openUrl(url) + } + + override fun onHowItWorksClick() { + urlOpener.openUrl("https://tangem.com/") // TODO replace with real link + } + + override fun onStartEarningClick() { + bottomSheetNavigation.activate(YieldSupplyPromoConfig.Action) + } +} \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/ui/YieldSupplyPromoContent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/ui/YieldSupplyPromoContent.kt new file mode 100644 index 0000000000..5839f6fb28 --- /dev/null +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/ui/YieldSupplyPromoContent.kt @@ -0,0 +1,239 @@ +package com.tangem.features.yield.supply.impl.promo.ui + +import android.content.res.Configuration +import androidx.annotation.DrawableRes +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.LinkAnnotation +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.withLink +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.components.* +import com.tangem.core.ui.components.label.Label +import com.tangem.core.ui.components.label.entity.LabelStyle +import com.tangem.core.ui.components.label.entity.LabelUM +import com.tangem.core.ui.extensions.* +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.features.yield.supply.impl.R +import com.tangem.features.yield.supply.impl.promo.entity.YieldSupplyPromoUM +import com.tangem.features.yield.supply.impl.promo.model.YieldSupplyPromoClickIntents +import com.tangem.utils.StringsSigns + +@Suppress("MagicNumber") +@Composable +internal fun YieldSupplyPromoContent( + yieldSupplyPromoUM: YieldSupplyPromoUM, + clickIntents: YieldSupplyPromoClickIntents, + modifier: Modifier = Modifier, +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = modifier + .background(color = TangemTheme.colors.background.tertiary) + .fillMaxWidth() + .imePadding() + .systemBarsPadding() + .padding(horizontal = 16.dp), + ) { + YieldStatusAppBar( + onBackClick = clickIntents::onBackClick, + onHowItWorksClick = clickIntents::onHowItWorksClick, + ) + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(horizontal = 20.dp), + ) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_analytics_up_24), + tint = TangemTheme.colors.icon.accent, + contentDescription = null, + modifier = Modifier + .background(TangemTheme.colors.icon.accent.copy(0.1f), CircleShape) + .padding(12.dp) + .size(32.dp), + ) + SpacerH(20.dp) + Text( + text = yieldSupplyPromoUM.title.resolveReference(), + style = TangemTheme.typography.h2, + color = TangemTheme.colors.text.primary1, + ) + SpacerH8() + Label( + state = LabelUM( + text = resourceReference(R.string.yield_module_promo_screen_variable_rate_info), + style = LabelStyle.REGULAR, + icon = R.drawable.ic_information_24, + onIconClick = clickIntents::onApyInfoClick, + ), + ) + SpacerH32() + PromoItems() + } + SpacerHMax() + YieldSupplyTosText( + tosLink = yieldSupplyPromoUM.tosLink, + policyLink = yieldSupplyPromoUM.policyLink, + onClick = clickIntents::onUrlClick, + ) + PrimaryButton( + text = stringResourceSafe(R.string.yield_module_start_earning), + onClick = clickIntents::onStartEarningClick, + modifier = Modifier.fillMaxWidth(), + ) + SpacerH8() + } +} + +@Composable +private fun YieldStatusAppBar(onBackClick: () -> Unit, onHowItWorksClick: () -> Unit) { + Row( + modifier = Modifier.padding(vertical = 16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_back_24), + contentDescription = null, + tint = TangemTheme.colors.icon.primary1, + modifier = Modifier.clickable(onClick = onBackClick), + ) + SpacerWMax() + Text( + text = stringResourceSafe(R.string.yield_module_promo_screen_how_it_works_button_title), + style = TangemTheme.typography.body1, + color = TangemTheme.colors.text.primary1, + modifier = Modifier.clickable(onClick = onHowItWorksClick), + ) + } +} + +@Composable +private fun PromoItems() { + PromoItem( + icon = R.drawable.ic_flash_new_24, + title = resourceReference(R.string.yield_module_promo_screen_cash_out_title), + subtitle = resourceReference(R.string.yield_module_promo_screen_cash_out_subtitle), + ) + SpacerH24() + PromoItem( + icon = R.drawable.ic_repeat_24, + title = resourceReference(R.string.yield_module_promo_screen_auto_balance_title), + subtitle = resourceReference(R.string.yield_module_promo_screen_auto_balance_subtitle), + ) + SpacerH24() + PromoItem( + icon = R.drawable.ic_security_check_24, + title = resourceReference(R.string.yield_module_promo_screen_self_custodial_title), + subtitle = resourceReference(R.string.yield_module_promo_screen_self_custodial_subtitle), + ) +} + +@Composable +private fun PromoItem(@DrawableRes icon: Int, title: TextReference, subtitle: TextReference) { + Row( + horizontalArrangement = Arrangement.spacedBy(20.dp), + ) { + Icon( + imageVector = ImageVector.vectorResource(icon), + contentDescription = null, + tint = TangemTheme.colors.icon.accent, + ) + Column(verticalArrangement = Arrangement.spacedBy(3.dp)) { + Text( + text = title.resolveReference(), + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + ) + Text( + text = subtitle.resolveReference(), + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.secondary, + ) + } + } +} + +@Composable +private fun YieldSupplyTosText(tosLink: String, policyLink: String, onClick: (String) -> Unit) { + val tosTitle = stringResourceSafe(R.string.common_terms_of_use) + val policyTitle = stringResourceSafe(R.string.common_privacy_policy) + val fullString = stringResourceSafe(id = R.string.yield_module_promo_screen_terms_disclaimer, tosTitle, policyTitle) + val tosIndex = fullString.indexOf(tosTitle) + val policyIndex = fullString.indexOf(policyTitle) + + Text( + text = buildAnnotatedString { + append(StringsSigns.POINT_SIGN) + appendSpace() + append(fullString.substring(0, tosIndex)) + withLink( + link = LinkAnnotation.Clickable( + tag = "TOS_TAG", + linkInteractionListener = { onClick(tosLink) }, + ), + block = { + appendColored( + text = fullString.substring(tosIndex, tosIndex + tosTitle.length), + color = TangemTheme.colors.text.accent, + ) + }, + ) + append(fullString.substring(tosIndex + tosTitle.length, policyIndex)) + withLink( + link = LinkAnnotation.Clickable( + tag = "POLICY_TAG", + linkInteractionListener = { onClick(policyLink) }, + ), + block = { + appendColored( + text = fullString.substring(policyIndex, policyIndex + policyTitle.length), + color = TangemTheme.colors.text.accent, + ) + }, + ) + }, + textAlign = TextAlign.Center, + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + ) +} + +// region Preview +@Composable +@Preview(showBackground = true, widthDp = 360, heightDp = 724) +@Preview(showBackground = true, widthDp = 360, heightDp = 724, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun YieldSupplyPromoContent_Preview() { + TangemThemePreview { + YieldSupplyPromoContent( + yieldSupplyPromoUM = YieldSupplyPromoUM( + tosLink = "https://tangem.com/terms-of-service/", + policyLink = "https://tangem.com/privacy-policy/", + title = resourceReference(R.string.yield_module_promo_screen_title, wrappedList("5.3")), + ), + clickIntents = object : YieldSupplyPromoClickIntents { + override fun onBackClick() {} + override fun onApyInfoClick() {} + override fun onHowItWorksClick() {} + override fun onStartEarningClick() {} + override fun onUrlClick(url: String) {} + }, + ) + } +} + +// endregion \ No newline at end of file