Updated on 2026-08-14
This commit is contained in:
parent
c5e437586b
commit
c5de0ebfdc
13 changed files with 452 additions and 2 deletions
|
|
@ -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<Params, YieldSupplyPromoComponent>
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.features.yield.supply.impl.promo
|
||||
|
||||
internal enum class YieldSupplyPromoConfig {
|
||||
Apy,
|
||||
Action,
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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<YieldSupplyPromoConfig> = 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue