diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 533cc30161..8be4d56fef 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -500,7 +500,7 @@
Последние
Получатель
Неверный адрес
- Убедитесь, что вы отправляете средства на адрес кошелька %s. Ошибки могут привести к потере ваших токенов
+ Убедитесь, что вы отправляете средства на адрес кошелька %s. Ошибки могут привести к потере ваших токенов.
Отправить
Мемо/ Код назначения - это код, разделяющий транзакции к общему получателю в сети криптовалют. Внимание: отсутствие мемо может привести к потере средств.
Мои кошельки
@@ -509,7 +509,7 @@
Отправка
Нажмите на любое поле, чтобы изменить его
Отправка %s
- Вы отправляете %1$s, включая комиссию сети %2$s
+ Вы отправляете **%1$s**, включая комиссию сети %2$s
Отправка %s
Всего
%1$s и %2$s будет отправлено
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 53b1c0d5b8..50421fa1b7 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -499,7 +499,7 @@
Recent
Recipient
Not a valid address
- Ensure that you are sending funds to an %s wallet address. Errors may result in the loss of your tokens
+ Ensure the receiving wallet address is on the %s network to avoid losing your tokens
Send to
A Memo/Destination Tag is a unique ID for differentiating transactions sent to the same recipient on the same network. Caution: Omitting a memo may lead to misplaced funds
My wallets
@@ -508,7 +508,7 @@
Sending...
Tap any field to change it
Send %s
- You are sending %1$s including a network fee of %2$s
+ You are sending **%1$s** including a network fee of %2$s
Sending %s
Total
%1$s and %2$s will be sent
diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts
index e2abfa0f4b..3129c8917b 100644
--- a/core/ui/build.gradle.kts
+++ b/core/ui/build.gradle.kts
@@ -45,4 +45,5 @@ dependencies {
implementation(deps.zxing.qrCore)
implementation(deps.jodatime)
implementation(deps.timber)
+ implementation(deps.markdown)
}
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt
index c095c2b8fd..521245b940 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt
@@ -94,10 +94,14 @@ fun AmountTextField(
SimpleTextField(
value = value,
onValueChange = { newText ->
- if (decimalFormat.isValidSymbols(newText)) {
- val trimmed = decimalFormat.getValidatedNumberWithFixedDecimals(newText, decimals)
- onValueChange(trimmed)
- }
+ onValueChange(
+ prepareEnter(
+ oldValue = value,
+ newValue = newText,
+ decimalFormat = decimalFormat,
+ decimals = decimals,
+ ),
+ )
},
textStyle = textStyle.copy(
fontSize = fontSize,
@@ -116,8 +120,37 @@ fun AmountTextField(
}
}
+private fun prepareEnter(oldValue: String, newValue: String, decimalFormat: DecimalFormat, decimals: Int): String {
+ val decimalSymbol = decimalFormat.decimalFormatSymbols.decimalSeparator
+ return if (decimalFormat.isValidSymbols(newValue)) {
+ val parsedValue = newValue.parseBigDecimalOrNull()?.toPlainString()
+ ?: if (newValue.isBlank()) "" else oldValue
+ val replacedWithSymbol = if (parsedValue.findLast { it != decimalSymbol } != null) {
+ when {
+ parsedValue.findLast { it == COMMA_SEPARATOR } != null -> {
+ parsedValue.replace(COMMA_SEPARATOR, decimalSymbol)
+ }
+ parsedValue.findLast { it == POINT_SEPARATOR } != null -> {
+ parsedValue.replace(POINT_SEPARATOR, decimalSymbol)
+ }
+ else -> parsedValue
+ }
+ } else {
+ parsedValue
+ }
+ val joinedSymbol = if (newValue.endsWith(COMMA_SEPARATOR) || newValue.endsWith(POINT_SEPARATOR)) {
+ replacedWithSymbol.plus(decimalSymbol)
+ } else {
+ replacedWithSymbol
+ }
+ decimalFormat.getValidatedNumberWithFixedDecimals(joinedSymbol, decimals)
+ } else {
+ oldValue
+ }
+}
+
private fun DecimalFormat.isValidSymbols(text: String): Boolean {
- return checkDecimalSeparatorDuplicate(text) && checkGroupingSeparator(text)
+ return checkDecimalSeparatorDuplicate(text)
}
// region preview
diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/MarkdownExtension.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/MarkdownExtension.kt
new file mode 100644
index 0000000000..408e9130ee
--- /dev/null
+++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/MarkdownExtension.kt
@@ -0,0 +1,56 @@
+package com.tangem.core.ui.extensions
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.withStyle
+import org.intellij.markdown.MarkdownElementTypes
+import org.intellij.markdown.ast.ASTNode
+import org.intellij.markdown.ast.getTextInNode
+import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
+import org.intellij.markdown.parser.MarkdownParser
+
+/** Markdown parser */
+@Composable
+fun rememberMarkdownParser() = remember {
+ MarkdownParser(CommonMarkFlavourDescriptor())
+}
+
+/**
+ * Styling markdown tree recursively
+ *
+ * @param markdownText original text
+ * @param node current processed node
+ */
+@Composable
+fun AnnotatedString.Builder.appendMarkdown(markdownText: String, node: ASTNode): AnnotatedString.Builder {
+ when (node.type) {
+ MarkdownElementTypes.MARKDOWN_FILE, MarkdownElementTypes.PARAGRAPH -> {
+ node.children.forEach { childNode ->
+ appendMarkdown(
+ markdownText = markdownText,
+ node = childNode,
+ )
+ }
+ }
+ MarkdownElementTypes.STRONG -> {
+ withStyle(SpanStyle(fontWeight = FontWeight.Medium)) {
+ node.children
+ .drop(2)
+ .dropLast(2)
+ .forEach { childNode ->
+ appendMarkdown(
+ markdownText = markdownText,
+ node = childNode,
+ )
+ }
+ }
+ }
+ else -> {
+ append(node.getTextInNode(markdownText).toString())
+ }
+ }
+ return this
+}
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt
index 766815d328..2f22994f64 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt
@@ -8,6 +8,9 @@ import androidx.compose.runtime.Immutable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.buildAnnotatedString
+import org.intellij.markdown.MarkdownElementTypes
/**
* Utility class for creating text as [String] or [StringRes].
@@ -160,6 +163,29 @@ fun TextReference.resolveReference(resources: Resources): String {
}
}
+/** Resolve [TextReference] as [AnnotatedString] */
+@Composable
+fun TextReference.resolveAnnotatedReference(): AnnotatedString {
+ return when (this) {
+ is TextReference.Res -> {
+ val args = formatArgs
+ .map { if (it is TextReference) it.resolveReference() else it }
+ .toTypedArray()
+
+ formatAnnotated(stringResource(id = id, *args))
+ }
+ is TextReference.PluralRes -> formatAnnotated(
+ pluralStringResource(id, count, *formatArgs.toTypedArray()),
+ )
+ is TextReference.Str -> formatAnnotated(value)
+ is TextReference.Combined -> buildAnnotatedString {
+ refs.forEach {
+ append(formatAnnotated(it.resolveReference()))
+ }
+ }
+ }
+}
+
/** Concatenate [this] reference with [ref] */
operator fun TextReference.plus(ref: TextReference): TextReference {
return when (this) {
@@ -169,4 +195,14 @@ operator fun TextReference.plus(ref: TextReference): TextReference {
is TextReference.Str,
-> TextReference.Combined(refs = wrappedList(this, ref))
}
+}
+
+@Composable
+private fun formatAnnotated(rawString: String): AnnotatedString {
+ val markdownDescriptor = rememberMarkdownParser()
+ val parsedTree = markdownDescriptor.parse(MarkdownElementTypes.MARKDOWN_FILE, rawString, true)
+
+ return buildAnnotatedString {
+ appendMarkdown(markdownText = rawString, node = parsedTree)
+ }
}
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/DecimalFormatterExt.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/DecimalFormatterExt.kt
index 5006df1d7a..2d685763e3 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/utils/DecimalFormatterExt.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/utils/DecimalFormatterExt.kt
@@ -10,9 +10,9 @@ import java.text.DecimalFormatSymbols
import java.util.Locale
private const val TEXT_CHUNK_THOUSAND = 3
-private const val POINT_SEPARATOR = '.'
-private const val COMMA_SEPARATOR = ','
private const val SCIENTIFIC_NOTATION = 'e'
+const val POINT_SEPARATOR = '.'
+const val COMMA_SEPARATOR = ','
const val DECIMAL_SEPARATOR_LIMIT = 1
@Composable
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt
index c4f1b95bdf..c3167929ad 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt
@@ -29,7 +29,10 @@ import com.tangem.core.ui.components.buttons.common.TangemButton
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
import com.tangem.core.ui.components.keyboardAsState
+import com.tangem.core.ui.extensions.resolveAnnotatedReference
+import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.shareText
+import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.send.impl.presentation.state.SendUiCurrentScreen
import com.tangem.features.send.impl.presentation.state.SendUiState
@@ -185,11 +188,17 @@ private fun SendingText(
currencySymbol = feeState.appCurrency.symbol,
currencyCode = feeState.appCurrency.code,
)
+ val textResource = remember(sendingValue, feeValue) {
+ resourceReference(
+ id = R.string.send_summary_transaction_description,
+ formatArgs = wrappedList(sendingValue, feeValue),
+ )
+ }
Text(
- text = stringResource(id = R.string.send_summary_transaction_description, sendingValue, feeValue),
+ text = textResource.resolveAnnotatedReference(),
textAlign = TextAlign.Center,
- style = TangemTheme.typography.caption1,
- color = TangemTheme.colors.text.tertiary,
+ style = TangemTheme.typography.caption2,
+ color = TangemTheme.colors.text.primary1,
modifier = Modifier
.fillMaxWidth()
.padding(TangemTheme.dimens.spacing12),
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelectorItem.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelectorItem.kt
index d9c609a5ca..d29fea2394 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelectorItem.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelectorItem.kt
@@ -16,6 +16,7 @@ import com.tangem.core.ui.components.SpacerWMax
import com.tangem.core.ui.components.rows.SelectorRowItem
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.BigDecimalFormatter
+import com.tangem.core.ui.utils.parseToBigDecimal
import com.tangem.features.send.impl.presentation.state.SendStates
import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState
import com.tangem.features.send.impl.presentation.state.fee.FeeType
@@ -114,11 +115,14 @@ private fun FeeError(feeSelectorState: FeeSelectorState) {
private fun FeeSelectorState.Content.getAmount(feeType: FeeType): Amount? {
val choosableFees = fees as? TransactionFee.Choosable
+ val decimals = fees.normal.amount.decimals
+ val customValue = this.customValues.firstOrNull()?.value?.parseToBigDecimal(decimals)
+ val customAmount = fees.normal.amount.copy(value = customValue)
return when (feeType) {
FeeType.Slow -> choosableFees?.minimum?.amount
FeeType.Market -> fees.normal.amount
FeeType.Fast -> choosableFees?.priority?.amount
- FeeType.Custom -> null
+ FeeType.Custom -> customAmount
}
}
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/utils/FormatterUtils.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/utils/FormatterUtils.kt
index 8854274b17..f2be7e6ed2 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/utils/FormatterUtils.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/utils/FormatterUtils.kt
@@ -11,6 +11,7 @@ import java.math.BigDecimal
import java.math.RoundingMode
private const val FIAT_DECIMALS = 2
+private const val CRYPTO_FEE_DECIMALS = 6
private const val FEE_MINIMUM_VALUE = 0.01
internal fun getCryptoReference(amount: Amount?, isFeeApproximate: Boolean): TextReference? {
@@ -21,7 +22,7 @@ internal fun getCryptoReference(amount: Amount?, isFeeApproximate: Boolean): Tex
BigDecimalFormatter.formatCryptoAmount(
cryptoAmount = amount.value,
cryptoCurrency = amount.currencySymbol,
- decimals = amount.decimals,
+ decimals = amount.decimals.coerceAtMost(CRYPTO_FEE_DECIMALS),
),
),
)
diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml
index 303ac01722..af0d8874b0 100644
--- a/gradle/dependencies.toml
+++ b/gradle/dependencies.toml
@@ -82,6 +82,7 @@ swipeRefreshLayout = "1.1.0"
spr-client = "3.6.2"
web3j = "4.10.1"
leakcanary = "2.13"
+markdown = "0.7.2"
# endregion Other libraries
# region Tangem
@@ -249,4 +250,5 @@ camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref =
camera-view = { module = "androidx.camera:camera-view", version.ref = "androidXCamera" }
web3j-core = { module = "org.web3j:core", version.ref = "web3j" }
leakcanary = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary" }
+markdown = { module = "org.jetbrains:markdown", version.ref = "markdown" }
# endregion Other