Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-24 16:20:00 +05:00
parent 6b187159d3
commit 29d6298fca
4 changed files with 109 additions and 20 deletions

View file

@ -11,6 +11,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
@ -20,9 +21,45 @@ import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TangemTooltip(text: String, content: @Composable (Modifier) -> Unit, modifier: Modifier = Modifier) {
InternalTangemTooltip(
modifier = modifier,
tooltipContent = {
Text(
modifier = Modifier.background(TangemTheme.colors.icon.secondary),
text = text,
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary2,
)
},
content = content,
)
}
@Composable
fun TangemTooltip(text: AnnotatedString, content: @Composable (Modifier) -> Unit, modifier: Modifier = Modifier) {
InternalTangemTooltip(
modifier = modifier,
tooltipContent = {
Text(
modifier = Modifier.background(TangemTheme.colors.icon.secondary),
text = text,
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary2,
)
},
content = content,
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun InternalTangemTooltip(
tooltipContent: @Composable () -> Unit,
content: @Composable (Modifier) -> Unit,
modifier: Modifier = Modifier,
) {
val tooltipState = rememberTooltipState(isPersistent = true)
val coroutineScope = rememberCoroutineScope()
TooltipBox(
@ -37,14 +74,7 @@ fun TangemTooltip(text: String, content: @Composable (Modifier) -> Unit, modifie
caretSize = DpSize(width = 14.dp, height = 8.dp),
contentColor = TangemTheme.colors.text.primary2,
containerColor = TangemTheme.colors.icon.secondary,
content = {
Text(
modifier = Modifier.background(TangemTheme.colors.icon.secondary),
text = text,
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary2,
)
},
content = { tooltipContent() },
)
},
content = {

View file

@ -75,6 +75,7 @@ internal class DefaultFeeSelectorBlockComponent @AssistedInject constructor(
FeeSelectorBlockContent(
state = state,
onReadMoreClick = model::onReadMoreClicked,
modifier = modifier
.conditional(params.feeDisplaySource == FeeSelectorParams.FeeDisplaySource.Screen) {
Modifier.clickable {

View file

@ -8,6 +8,7 @@ import com.tangem.blockchain.common.AmountType
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.navigation.url.UrlOpener
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase
@ -26,6 +27,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import java.util.Locale
import javax.inject.Inject
@Suppress("LongParameterList")
@ -33,13 +35,14 @@ import javax.inject.Inject
@ModelScoped
internal class FeeSelectorModel @Inject constructor(
paramsContainer: ParamsContainer,
private val urlOpener: UrlOpener,
private val isFeeApproximateUseCase: IsFeeApproximateUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
override val dispatchers: CoroutineDispatcherProvider,
private val feeSelectorReloadListener: FeeSelectorReloadListener,
private val feeSelectorCheckReloadListener: FeeSelectorCheckReloadListener,
private val feeSelectorCheckReloadTrigger: FeeSelectorCheckReloadTrigger,
private val feeSelectorAlertFactory: FeeSelectorAlertFactory,
override val dispatchers: CoroutineDispatcherProvider,
) : Model(), FeeSelectorIntents, FeeSelectorModelCallback {
private val params = paramsContainer.require<FeeSelectorParams>()
@ -62,6 +65,16 @@ internal class FeeSelectorModel @Inject constructor(
uiState.value = feeSelectorUM
}
fun onReadMoreClicked() {
val locale = if (Locale.getDefault().language == RU_LOCALE) RU_LOCALE else EN_LOCALE
val url = buildString {
append(FEE_READ_MORE_URL_FIRST_PART)
append(locale)
append(FEE_READ_MORE_URL_SECOND_PART)
}
urlOpener.openUrl(url)
}
private fun initAppCurrency() {
modelScope.launch {
appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default }
@ -174,4 +187,11 @@ internal class FeeSelectorModel @Inject constructor(
)
}
}
private companion object {
const val RU_LOCALE = "ru"
const val EN_LOCALE = "en"
const val FEE_READ_MORE_URL_FIRST_PART = "https://tangem.com/"
const val FEE_READ_MORE_URL_SECOND_PART = "/blog/post/what-is-a-transaction-fee-and-why-do-we-need-it/"
}
}

View file

@ -6,9 +6,11 @@ import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.*
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Devices
@ -23,6 +25,7 @@ import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.ui.amountScreen.utils.getFiatString
import com.tangem.core.ui.components.TextShimmer
import com.tangem.core.ui.components.atoms.text.EllipsisText
import com.tangem.core.ui.components.tooltip.TangemTooltip
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.format.bigdecimal.BigDecimalFormatConstants.EMPTY_BALANCE_SIGN
import com.tangem.core.ui.format.bigdecimal.crypto
@ -37,8 +40,14 @@ import com.tangem.features.send.v2.impl.R
import kotlinx.collections.immutable.persistentListOf
import java.math.BigDecimal
private const val READ_MORE_TAG = "READ_MORE"
@Composable
internal fun FeeSelectorBlockContent(state: FeeSelectorUM, modifier: Modifier = Modifier) {
internal fun FeeSelectorBlockContent(
state: FeeSelectorUM,
onReadMoreClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier
.background(TangemTheme.colors.background.action)
@ -51,14 +60,14 @@ internal fun FeeSelectorBlockContent(state: FeeSelectorUM, modifier: Modifier =
contentDescription = null,
tint = TangemTheme.colors.icon.accent,
)
FeeSelectorDescription(state = state)
FeeSelectorDescription(state = state, onReadMoreClick = onReadMoreClick)
}
}
@Composable
private fun FeeSelectorDescription(state: FeeSelectorUM, modifier: Modifier = Modifier) {
private fun FeeSelectorDescription(state: FeeSelectorUM, onReadMoreClick: () -> Unit, modifier: Modifier = Modifier) {
Row(modifier = modifier, horizontalArrangement = Arrangement.SpaceBetween) {
FeeSelectorStaticPart(modifier = Modifier.weight(1f))
FeeSelectorStaticPart(modifier = Modifier.weight(1f), onReadMoreClick = onReadMoreClick)
when (state) {
is FeeSelectorUM.Content -> FeeContent(state)
is FeeSelectorUM.Loading -> FeeLoading()
@ -68,7 +77,7 @@ private fun FeeSelectorDescription(state: FeeSelectorUM, modifier: Modifier = Mo
}
@Composable
private fun FeeSelectorStaticPart(modifier: Modifier = Modifier) {
private fun FeeSelectorStaticPart(onReadMoreClick: () -> Unit, modifier: Modifier = Modifier) {
Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) {
Text(
modifier = Modifier
@ -80,13 +89,42 @@ private fun FeeSelectorStaticPart(modifier: Modifier = Modifier) {
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Icon(
val linkText = stringResourceSafe(R.string.common_read_more)
val fullString = stringResourceSafe(R.string.common_fee_selector_footer, linkText)
val linkTextPosition = fullString.length - linkText.length
val defaultStyle = TangemTheme.colors.text.primary2
val linkStyle = TangemTheme.colors.text.accent
val annotatedString = remember(defaultStyle, linkStyle) {
buildAnnotatedString {
withStyle(SpanStyle(defaultStyle)) {
append(fullString.substring(0, linkTextPosition))
}
withLink(
link = LinkAnnotation.Clickable(
tag = READ_MORE_TAG,
linkInteractionListener = { onReadMoreClick() },
),
block = {
withStyle(SpanStyle(linkStyle)) {
append(fullString.substring(linkTextPosition, fullString.length))
}
},
)
}
}
TangemTooltip(
text = annotatedString,
modifier = Modifier
.padding(start = TangemTheme.dimens.spacing6)
.size(TangemTheme.dimens.size16),
painter = painterResource(id = R.drawable.ic_token_info_24),
contentDescription = null,
tint = TangemTheme.colors.icon.informative,
content = { contentModifier ->
Icon(
modifier = contentModifier.size(TangemTheme.dimens.size16),
painter = painterResource(id = R.drawable.ic_token_info_24),
contentDescription = null,
tint = TangemTheme.colors.icon.informative,
)
},
)
}
}
@ -148,7 +186,7 @@ private fun FeeContent(state: FeeSelectorUM.Content, modifier: Modifier = Modifi
@Composable
private fun FeeSelectorBlockContent_Preview(@PreviewParameter(FeeSelectorUMProvider::class) state: FeeSelectorUM) {
TangemThemePreview {
FeeSelectorBlockContent(modifier = Modifier.fillMaxWidth(), state = state)
FeeSelectorBlockContent(modifier = Modifier.fillMaxWidth(), state = state, onReadMoreClick = {})
}
}