Updated on 2026-08-14

This commit is contained in:
Tangem 2025-06-24 18:12:45 +05:00
commit 1cecd561dc
16 changed files with 741 additions and 15 deletions

View file

@ -62,7 +62,7 @@ fun MessageBottomSheetV2(state: MessageBottomSheetUMV2, onDismissRequest: () ->
}
@Composable
private fun MessageBottomSheetV2Content(state: MessageBottomSheetUMV2, modifier: Modifier = Modifier) {
fun MessageBottomSheetV2Content(state: MessageBottomSheetUMV2, modifier: Modifier = Modifier) {
Column(modifier = modifier) {
state.elements.fastForEach {
when (it) {

View file

@ -14,6 +14,7 @@ android {
dependencies {
/* Project - API */
implementation(projects.features.manageTokens.api)
implementation(projects.features.swapV2.api)
/* Project - Core */
implementation(projects.core.decompose)

View file

@ -7,20 +7,27 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.arkivanov.decompose.router.slot.childSlot
import com.arkivanov.decompose.router.slot.dismiss
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.features.managetokens.choosetoken.entity.ChooseManageTokensBottomSheetConfig
import com.tangem.features.managetokens.choosetoken.model.ChooseManagedTokensModel
import com.tangem.features.managetokens.choosetoken.ui.ChooseManagedTokenContent
import com.tangem.features.managetokens.component.ChooseManagedTokensComponent
import com.tangem.features.swap.v2.api.choosetoken.SwapChooseTokenNetworkComponent
import com.tangem.features.swap.v2.api.choosetoken.SwapChooseTokenNetworkTrigger
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.launch
internal class DefaultChooseManagedTokensComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
@Assisted private val params: ChooseManagedTokensComponent.Params,
private val swapChooseTokenNetworkFactory: SwapChooseTokenNetworkComponent.Factory,
private val swapChooseTokenNetworkTrigger: SwapChooseTokenNetworkTrigger,
) : ChooseManagedTokensComponent, AppComponentContext by context {
private val model: ChooseManagedTokensModel = getOrCreateModel(params)
@ -48,18 +55,21 @@ internal class DefaultChooseManagedTokensComponent @AssistedInject constructor(
config: ChooseManageTokensBottomSheetConfig,
componentContext: ComponentContext,
): ComposableBottomSheetComponent = when (config) {
else -> getStubComponent()
}
private fun getStubComponent() = StubComponent()
class StubComponent : ComposableBottomSheetComponent {
override fun dismiss() {}
@Composable
override fun BottomSheet() {
/* no-op */
}
is ChooseManageTokensBottomSheetConfig.SwapTokensBottomSheetConfig -> swapChooseTokenNetworkFactory.create(
context = childByContext(componentContext),
params = SwapChooseTokenNetworkComponent.Params(
userWalletId = config.userWalletId,
initialCurrency = config.initialCurrency,
token = config.token,
onDismiss = model.bottomSheetNavigation::dismiss,
onResult = {
componentScope.launch {
swapChooseTokenNetworkTrigger.trigger(it)
}
router.pop()
},
),
)
}
@AssistedFactory

View file

@ -30,6 +30,7 @@ import com.tangem.pagination.BatchFetchResult
import com.tangem.pagination.PaginationStatus
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
@ -142,7 +143,11 @@ internal class ChooseManagedTokensModel @Inject constructor(
private fun updateItems(items: ImmutableList<CurrencyItemUM>) {
uiState.update { state ->
state.copy(
readContent = state.readContent.copy(items = items),
readContent = state.readContent.copy(
items = items.filterNot {
it.id.value == params.initialCurrency.id.rawCurrencyId?.value
}.toPersistentList(),
),
)
}
}

View file

@ -0,0 +1,20 @@
package com.tangem.features.swap.v2.api.choosetoken
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
interface SwapChooseTokenNetworkComponent : ComposableBottomSheetComponent {
data class Params(
val userWalletId: UserWalletId,
val initialCurrency: CryptoCurrency,
val token: ManagedCryptoCurrency.Token,
val onDismiss: () -> Unit,
val onResult: (CryptoCurrency) -> Unit,
)
interface Factory : ComponentFactory<Params, SwapChooseTokenNetworkComponent>
}

View file

@ -0,0 +1,11 @@
package com.tangem.features.swap.v2.api.choosetoken
import com.tangem.domain.models.currency.CryptoCurrency
/**
* Triggers swap token selection
*/
interface SwapChooseTokenNetworkTrigger {
suspend fun trigger(cryptoCurrency: CryptoCurrency)
}

View file

@ -0,0 +1,46 @@
package com.tangem.features.swap.v2.impl.choosetoken.di
import com.tangem.core.decompose.di.ModelComponent
import com.tangem.core.decompose.model.Model
import com.tangem.features.swap.v2.api.choosetoken.SwapChooseTokenNetworkComponent
import com.tangem.features.swap.v2.api.choosetoken.SwapChooseTokenNetworkTrigger
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.DefaultSwapChooseTokenNetworkComponent
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.DefaultSwapChooseTokenNetworkTrigger
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.SwapChooseTokenNetworkListener
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.model.SwapChooseTokenNetworkModel
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
@InstallIn(ModelComponent::class)
@Module
internal interface SwapChooseTokenModule {
@Binds
@IntoMap
@ClassKey(SwapChooseTokenNetworkModel::class)
fun provideSwapChooseTokenNetworkModel(impl: SwapChooseTokenNetworkModel): Model
}
@Module
@InstallIn(SingletonComponent::class)
internal interface SwapChooseTokenModuleBinds {
@Binds
@Singleton
fun provideSwapChooseTokenNetworksBottomSheetComponent(
impl: DefaultSwapChooseTokenNetworkComponent.Factory,
): SwapChooseTokenNetworkComponent.Factory
@Binds
@Singleton
fun bindSwapChooseTokenNetworkTrigger(impl: DefaultSwapChooseTokenNetworkTrigger): SwapChooseTokenNetworkTrigger
@Binds
@Singleton
fun bindSwapChooseTokenNetworkListener(impl: DefaultSwapChooseTokenNetworkTrigger): SwapChooseTokenNetworkListener
}

View file

@ -0,0 +1,42 @@
package com.tangem.features.swap.v2.impl.choosetoken.fromSupported
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.features.swap.v2.api.choosetoken.SwapChooseTokenNetworkComponent
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.model.SwapChooseTokenNetworkModel
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.ui.SwapChooseTokenNetworkBottomSheet
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultSwapChooseTokenNetworkComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
@Assisted private val params: SwapChooseTokenNetworkComponent.Params,
) : SwapChooseTokenNetworkComponent, AppComponentContext by context {
private val model: SwapChooseTokenNetworkModel = getOrCreateModel(params)
override fun dismiss() {
params.onDismiss()
}
@Composable
override fun BottomSheet() {
val state by model.uiState.collectAsStateWithLifecycle()
SwapChooseTokenNetworkBottomSheet(
config = state.bottomSheetConfig,
)
}
@AssistedFactory
interface Factory : SwapChooseTokenNetworkComponent.Factory {
override fun create(
context: AppComponentContext,
params: SwapChooseTokenNetworkComponent.Params,
): DefaultSwapChooseTokenNetworkComponent
}
}

View file

@ -0,0 +1,28 @@
package com.tangem.features.swap.v2.impl.choosetoken.fromSupported
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.swap.v2.api.choosetoken.SwapChooseTokenNetworkTrigger
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import javax.inject.Inject
import javax.inject.Singleton
/**
* Listens to swap token selection
*/
interface SwapChooseTokenNetworkListener {
val swapChooseTokenNetworkResultFlow: SharedFlow<CryptoCurrency>
}
@Singleton
internal class DefaultSwapChooseTokenNetworkTrigger @Inject constructor() :
SwapChooseTokenNetworkTrigger,
SwapChooseTokenNetworkListener {
override val swapChooseTokenNetworkResultFlow: SharedFlow<CryptoCurrency>
field = MutableSharedFlow<CryptoCurrency>()
override suspend fun trigger(result: CryptoCurrency) {
swapChooseTokenNetworkResultFlow.emit(result)
}
}

View file

@ -0,0 +1,40 @@
package com.tangem.features.swap.v2.impl.choosetoken.fromSupported.entity
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2
import com.tangem.core.ui.extensions.TextReference
import kotlinx.collections.immutable.ImmutableList
internal data class SwapChooseTokenNetworkUM(
val bottomSheetConfig: TangemBottomSheetConfig,
)
@Immutable
internal sealed class SwapChooseTokenNetworkContentUM : TangemBottomSheetConfigContent {
abstract val messageContent: MessageBottomSheetUMV2
data class Loading(
override val messageContent: MessageBottomSheetUMV2,
) : SwapChooseTokenNetworkContentUM()
data class Error(
override val messageContent: MessageBottomSheetUMV2,
) : SwapChooseTokenNetworkContentUM()
data class Content(
override val messageContent: MessageBottomSheetUMV2,
val swapNetworks: ImmutableList<SwapChooseNetworkUM>,
) : SwapChooseTokenNetworkContentUM()
}
internal data class SwapChooseNetworkUM(
val title: TextReference,
val subtitle: TextReference,
@DrawableRes val iconResId: Int,
val hasFixedRate: Boolean,
val onNetworkClick: () -> Unit,
)

View file

@ -0,0 +1,26 @@
package com.tangem.features.swap.v2.impl.choosetoken.fromSupported.model
import com.tangem.core.ui.components.bottomsheets.message.*
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.features.swap.v2.impl.R
internal object SwapChooseTokenFactory {
fun getErrorMessage(tokenName: String, onDismiss: () -> Unit): MessageBottomSheetUMV2 {
return messageBottomSheetUM {
infoBlock {
icon(R.drawable.img_attention_20) {
type = MessageBottomSheetUMV2.Icon.Type.Attention
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
}
title = resourceReference(R.string.express_swap_not_supported_title, wrappedList(tokenName))
body = resourceReference(R.string.express_swap_not_supported_text)
}
secondaryButton {
text = resourceReference(R.string.warning_button_ok)
onClick { onDismiss() }
}
}
}
}

View file

@ -0,0 +1,118 @@
package com.tangem.features.swap.v2.impl.choosetoken.fromSupported.model
import arrow.core.getOrElse
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage
import com.tangem.domain.managetokens.CreateCryptoCurrencyUseCase
import com.tangem.domain.swap.usecase.GetSwapSupportedPairsUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.swap.v2.api.choosetoken.SwapChooseTokenNetworkComponent
import com.tangem.features.swap.v2.impl.R
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.entity.SwapChooseTokenNetworkContentUM
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.entity.SwapChooseTokenNetworkUM
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.model.SwapChooseTokenFactory.getErrorMessage
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.model.transformers.SwapChooseContentStateTransformer
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.model.transformers.SwapChooseErrorStateTransformer
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.transformer.update
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@ModelScoped
internal class SwapChooseTokenNetworkModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
paramsContainer: ParamsContainer,
private val getSwapSupportedPairsUseCase: GetSwapSupportedPairsUseCase,
private val createCryptoCurrencyUseCase: CreateCryptoCurrencyUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val uiMessageSender: UiMessageSender,
) : Model() {
private val params: SwapChooseTokenNetworkComponent.Params = paramsContainer.require()
val uiState: StateFlow<SwapChooseTokenNetworkUM>
field: MutableStateFlow<SwapChooseTokenNetworkUM> = MutableStateFlow(
SwapChooseTokenNetworkUM(
TangemBottomSheetConfig(
isShown = true,
onDismissRequest = params.onDismiss,
content = SwapChooseTokenNetworkContentUM.Loading(
messageContent = getErrorMessage(
tokenName = params.token.name,
onDismiss = params.onDismiss,
),
),
),
),
)
init {
initContent()
}
private fun initContent() {
val userWallet = getUserWalletUseCase(params.userWalletId).getOrElse {
Timber.e("Failed to get user wallet: $it")
getGenericErrorState()
return
}
modelScope.launch {
val cryptoCurrencyList = createCryptoCurrencyUseCase(
token = params.token,
userWalletId = params.userWalletId,
).getOrElse {
Timber.e("Failed to get crypto currency")
getGenericErrorState()
return@launch
}
val pairs = getSwapSupportedPairsUseCase.invoke(
userWallet = userWallet,
initialCurrency = params.initialCurrency,
cryptoCurrencyList = cryptoCurrencyList + params.initialCurrency,
).getOrElse {
Timber.e(it.toString())
uiState.update(
SwapChooseErrorStateTransformer(
tokenName = params.token.name,
onDismiss = params.onDismiss,
),
)
return@launch
}
delay(MINIMUM_LOADING_TIME)
uiState.update(
SwapChooseContentStateTransformer(
pairs = pairs,
onNetworkClick = params.onResult,
tokenName = params.token.name,
onDismiss = params.onDismiss,
),
)
}
}
private fun getGenericErrorState() {
uiMessageSender.send(
DialogMessage(
title = resourceReference(id = R.string.common_error),
message = resourceReference(id = R.string.common_unknown_error),
onDismissRequest = params.onDismiss,
firstActionBuilder = { okAction() },
),
)
}
private companion object {
const val MINIMUM_LOADING_TIME = 100L
}
}

View file

@ -0,0 +1,54 @@
package com.tangem.features.swap.v2.impl.choosetoken.fromSupported.model.transformers
import com.tangem.core.ui.extensions.iconResId
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.express.models.ExpressRateType
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.swap.models.SwapCurrencies
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.entity.SwapChooseNetworkUM
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.entity.SwapChooseTokenNetworkContentUM
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.entity.SwapChooseTokenNetworkUM
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.model.SwapChooseTokenFactory.getErrorMessage
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.toPersistentList
internal class SwapChooseContentStateTransformer(
private val pairs: SwapCurrencies,
private val tokenName: String,
private val onNetworkClick: (CryptoCurrency) -> Unit,
private val onDismiss: () -> Unit,
) : Transformer<SwapChooseTokenNetworkUM> {
override fun transform(prevState: SwapChooseTokenNetworkUM): SwapChooseTokenNetworkUM {
val swapNetworks = pairs.fromGroup.available.map {
val network = it.currencyStatus.currency.network
SwapChooseNetworkUM(
title = stringReference(network.name),
subtitle = stringReference(network.standardType.name),
iconResId = network.iconResId,
hasFixedRate = it.providers.any { it.rateTypes.any { it == ExpressRateType.Fixed } },
onNetworkClick = { onNetworkClick(it.currencyStatus.currency) },
)
}.toPersistentList()
return prevState.copy(
bottomSheetConfig = prevState.bottomSheetConfig.copy(
content = if (swapNetworks.isNotEmpty()) {
SwapChooseTokenNetworkContentUM.Content(
swapNetworks = swapNetworks,
messageContent = getErrorMessage(
tokenName = tokenName,
onDismiss = onDismiss,
),
)
} else {
SwapChooseTokenNetworkContentUM.Error(
messageContent = getErrorMessage(
tokenName = tokenName,
onDismiss = onDismiss,
),
)
},
),
)
}
}

View file

@ -0,0 +1,24 @@
package com.tangem.features.swap.v2.impl.choosetoken.fromSupported.model.transformers
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.entity.SwapChooseTokenNetworkContentUM
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.entity.SwapChooseTokenNetworkUM
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.model.SwapChooseTokenFactory.getErrorMessage
import com.tangem.utils.transformer.Transformer
internal class SwapChooseErrorStateTransformer(
private val tokenName: String,
private val onDismiss: () -> Unit,
) : Transformer<SwapChooseTokenNetworkUM> {
override fun transform(prevState: SwapChooseTokenNetworkUM): SwapChooseTokenNetworkUM {
return prevState.copy(
bottomSheetConfig = prevState.bottomSheetConfig.copy(
content = SwapChooseTokenNetworkContentUM.Error(
messageContent = getErrorMessage(
tokenName = tokenName,
onDismiss = onDismiss,
),
),
),
)
}
}

View file

@ -0,0 +1,301 @@
package com.tangem.features.swap.v2.impl.choosetoken.fromSupported.ui
import android.content.res.Configuration
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import com.tangem.core.ui.components.SpacerWMax
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.message.*
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.swap.v2.impl.R
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.entity.SwapChooseNetworkUM
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.entity.SwapChooseTokenNetworkContentUM
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
@Composable
internal fun SwapChooseTokenNetworkBottomSheet(config: TangemBottomSheetConfig) {
TangemModalBottomSheet<SwapChooseTokenNetworkContentUM>(
config = config,
containerColor = TangemTheme.colors.background.tertiary,
title = {
AnimatedContent(
targetState = config.content is SwapChooseTokenNetworkContentUM.Content,
) { isContent ->
TangemModalBottomSheetTitle(
title = resourceReference(R.string.common_choose_network).takeIf { isContent },
endIconRes = R.drawable.ic_close_24,
onEndClick = config.onDismissRequest,
)
}
},
content = { contentConfig ->
SwapChooseTokenNetworkContent(contentConfig)
},
)
}
@Composable
internal fun SwapChooseTokenNetworkContent(state: SwapChooseTokenNetworkContentUM) {
AnimatedContent(
targetState = state,
transitionSpec = { fadeIn().togetherWith(fadeOut()) },
) { targetState ->
when (targetState) {
is SwapChooseTokenNetworkContentUM.Content -> SwapChooseTokenNetworkContentList(targetState.swapNetworks)
else -> {
Box {
MessageBottomSheetV2Content(
state = targetState.messageContent,
modifier = Modifier
.conditional(targetState is SwapChooseTokenNetworkContentUM.Loading) {
alpha(0f)
},
)
if (targetState is SwapChooseTokenNetworkContentUM.Loading) {
CircularProgressIndicator(
color = TangemTheme.colors.icon.inactive,
modifier = Modifier
.align(Alignment.Center),
)
}
}
}
}
}
}
@Composable
private fun SwapChooseTokenNetworkContentList(swapNetworks: ImmutableList<SwapChooseNetworkUM>) {
Column {
swapNetworks.fastForEach { network ->
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp),
modifier = Modifier
.fillMaxWidth()
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = ripple(),
onClick = { network.onNetworkClick() },
)
.padding(
vertical = 12.dp,
horizontal = 14.dp,
),
) {
Image(
modifier = Modifier.size(36.dp),
painter = painterResource(id = network.iconResId),
contentDescription = null,
)
Text(
text = network.title.resolveReference(),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.primary1,
)
Text(
text = network.subtitle.resolveReference(),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
)
if (network.hasFixedRate) {
SpacerWMax()
Text(
text = stringResourceSafe(R.string.swap_fixed_rate),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier
.border(
width = 1.dp,
color = TangemTheme.colors.stroke.primary,
shape = RoundedCornerShape(6.dp),
)
.padding(
vertical = 2.dp,
horizontal = 4.dp,
),
)
}
}
}
}
}
// region Preview
@Composable
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun SwapChooseTokenNetworkContent_Preview(
@PreviewParameter(PreviewProvider::class) params: SwapChooseTokenNetworkContentUM,
) {
TangemThemePreview {
SwapChooseTokenNetworkContent(state = params)
}
}
private class PreviewProvider : PreviewParameterProvider<SwapChooseTokenNetworkContentUM> {
override val values: Sequence<SwapChooseTokenNetworkContentUM>
get() = sequenceOf(
SwapChooseTokenNetworkContentUM.Loading(
messageContent = messageBottomSheetUM {
infoBlock {
icon(R.drawable.ic_alert_triangle_20) {
type = MessageBottomSheetUMV2.Icon.Type.Attention
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.Unspecified
}
title = stringReference("Shiba Inu is not suppoerted")
body = stringReference("This token is not supported. Please choose a different token to swap.")
}
secondaryButton {
text = resourceReference(R.string.warning_button_ok)
onClick { closeBs() }
}
},
),
SwapChooseTokenNetworkContentUM.Error(
messageContent = messageBottomSheetUM {
infoBlock {
icon(R.drawable.ic_alert_triangle_20) {
type = MessageBottomSheetUMV2.Icon.Type.Attention
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
}
title = stringReference("Shiba Inu is not supported")
body = stringReference("This token is not supported. Please choose a different token to swap.")
}
secondaryButton {
text = resourceReference(R.string.warning_button_ok)
onClick { closeBs() }
}
},
),
SwapChooseTokenNetworkContentUM.Content(
messageContent = messageBottomSheetUM {
infoBlock {
icon(R.drawable.img_attention_20) {
type = MessageBottomSheetUMV2.Icon.Type.Attention
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
}
title = stringReference("Shiba Inu is not suppoerted")
body = stringReference("This token is not supported. Please choose a different token to swap.")
}
secondaryButton {
text = resourceReference(R.string.warning_button_ok)
onClick { closeBs() }
}
},
swapNetworks = persistentListOf(
SwapChooseNetworkUM(
title = stringReference("Cardano"),
subtitle = stringReference("MAIN"),
iconResId = R.drawable.img_cardano_22,
hasFixedRate = false,
onNetworkClick = {},
),
),
),
SwapChooseTokenNetworkContentUM.Content(
messageContent = messageBottomSheetUM {
infoBlock {
icon(R.drawable.img_attention_20) {
type = MessageBottomSheetUMV2.Icon.Type.Attention
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
}
title = stringReference("Shiba Inu is not suppoerted")
body = stringReference("This token is not supported. Please choose a different token to swap.")
}
secondaryButton {
text = resourceReference(R.string.warning_button_ok)
onClick { closeBs() }
}
},
swapNetworks = persistentListOf(
SwapChooseNetworkUM(
title = stringReference("Cardano"),
subtitle = stringReference("MAIN"),
iconResId = R.drawable.img_cardano_22,
hasFixedRate = false,
onNetworkClick = {},
),
SwapChooseNetworkUM(
title = stringReference("BNB Smart Chain"),
subtitle = stringReference("BEP20"),
iconResId = R.drawable.img_bsc_22,
hasFixedRate = false,
onNetworkClick = {},
),
SwapChooseNetworkUM(
title = stringReference("BNB Smart Chain"),
subtitle = stringReference("BEP20"),
iconResId = R.drawable.img_bsc_22,
hasFixedRate = true,
onNetworkClick = {},
),
SwapChooseNetworkUM(
title = stringReference("BNB Smart Chain"),
subtitle = stringReference("BEP20"),
iconResId = R.drawable.img_bsc_22,
hasFixedRate = false,
onNetworkClick = {},
),
),
),
SwapChooseTokenNetworkContentUM.Content(
messageContent = messageBottomSheetUM {
infoBlock {
icon(R.drawable.img_attention_20) {
type = MessageBottomSheetUMV2.Icon.Type.Attention
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
}
title = stringReference("Shiba Inu is not suppoerted")
body = stringReference("This token is not supported. Please choose a different token to swap.")
}
secondaryButton {
text = resourceReference(R.string.warning_button_ok)
onClick { closeBs() }
}
},
swapNetworks = buildList {
repeat(10) {
add(
SwapChooseNetworkUM(
title = stringReference("Cardano"),
subtitle = stringReference("MAIN"),
iconResId = R.drawable.img_cardano_22,
hasFixedRate = false,
onNetworkClick = {},
),
)
}
}.toPersistentList(),
),
)
}
// endregion

View file

@ -60,7 +60,7 @@ fun SwapSelectTokenScreen(state: SwapSelectTokenStateHolder, onBack: () -> Unit)
},
topBar = {
ExpandableSearchView(
title = stringResourceSafe(R.string.swapping_token_list_title),
title = stringResourceSafe(R.string.common_choose_token),
onBackClick = onBack,
placeholderSearchText = stringResourceSafe(id = R.string.common_search_tokens),
onSearchChange = state.onSearchEntered,