Updated on 2026-08-14

This commit is contained in:
Tangem 2023-12-08 14:51:11 +03:00
parent 56c32291f2
commit a19b9c01b5
8 changed files with 61 additions and 54 deletions

View file

@ -438,6 +438,7 @@
<string name="send_amount_label">Amount</string>
<string name="send_amount_substract">Subtract from send amount</string>
<string name="send_amount_substract_footer">The recipient will receive %s</string>
<string name="send_confirm_label">Confirm</string>
<string name="send_date_format">%1$s at %2$s</string>
<string name="send_destination_hint_address">Address</string>
<string name="send_destination_tag_field">Destination Tag</string>
@ -488,6 +489,7 @@
<string name="send_qrcode_scan_info">Please align your QR code with the square to scan it. Ensure you scan %s network address.</string>
<string name="send_recent_transactions">Recent</string>
<string name="send_recipient">Recipient</string>
<string name="send_recipient_label">Send to</string>
<string name="send_recipient_address_error">Not a valid address</string>
<string name="send_recipient_address_footer">Ensure that you are sending funds to an %s wallet address. Errors may result in the loss of your tokens</string>
<string name="send_recipient_memo_footer">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</string>

View file

@ -11,7 +11,8 @@ internal sealed class SendRecipientListContent {
val id: String,
val title: TextReference,
val subtitle: TextReference,
val info: TextReference? = null,
val timestamp: TextReference? = null,
val subtitleEndOffset: Int = 0,
@DrawableRes val subtitleIconRes: Int? = null,
) : SendRecipientListContent()

View file

@ -8,7 +8,7 @@ import java.lang.ref.WeakReference
internal class StateRouter(
private val fragmentManager: WeakReference<FragmentManager>,
) {
var currentState: MutableStateFlow<SendUiStateType> = MutableStateFlow(SendUiStateType.Amount)
var currentState: MutableStateFlow<SendUiStateType> = MutableStateFlow(SendUiStateType.Recipient)
private set
private var isFromSend: Boolean = false
@ -22,9 +22,9 @@ internal class StateRouter(
showSend()
} else {
when (currentState.value) {
SendUiStateType.Amount -> popBackStack()
SendUiStateType.Recipient -> showAmount()
SendUiStateType.Fee -> showRecipient()
SendUiStateType.Recipient -> popBackStack()
SendUiStateType.Amount -> showRecipient()
SendUiStateType.Fee -> showAmount()
SendUiStateType.Send -> showFee()
}
}
@ -32,8 +32,8 @@ internal class StateRouter(
fun onNextClick() {
when (currentState.value) {
SendUiStateType.Amount -> showRecipient()
SendUiStateType.Recipient -> showFee()
SendUiStateType.Recipient -> showAmount()
SendUiStateType.Amount -> showFee()
SendUiStateType.Fee -> showSend()
SendUiStateType.Send -> onBackClick()
}
@ -41,9 +41,9 @@ internal class StateRouter(
fun onPrevClick() {
when (currentState.value) {
SendUiStateType.Amount -> popBackStack()
SendUiStateType.Recipient -> showAmount()
SendUiStateType.Fee -> showRecipient()
SendUiStateType.Recipient -> popBackStack()
SendUiStateType.Amount -> showRecipient()
SendUiStateType.Fee -> showAmount()
SendUiStateType.Send -> popBackStack()
}
}
@ -63,7 +63,7 @@ internal class StateRouter(
currentState.update { SendUiStateType.Fee }
}
fun showSend() {
private fun showSend() {
currentState.update { SendUiStateType.Send }
}
}

View file

@ -3,10 +3,12 @@ package com.tangem.features.send.impl.presentation.state.recipient
import androidx.paging.*
import com.tangem.common.Provider
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.utils.DateTimeFormatters
import com.tangem.core.ui.utils.toDateFormat
import com.tangem.core.ui.utils.toTimeFormat
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.features.send.impl.R
@ -34,6 +36,7 @@ internal class SendRecipientListConverter(
val walletsItem = getWalletItems(filteredWallets, txHistoryCount)
val cryptoCurrency = cryptoCurrencyStatusProvider().currency
currentStateProvider().recipientList.update {
if (txHistoryCount == 0) {
PagingData.from(listOf(walletsItem))
@ -51,8 +54,9 @@ internal class SendRecipientListConverter(
SendRecipientListContent.Item(
id = tx.txHash,
title = tx.extractAddress(),
subtitle = TextReference.Str(tx.getAmount()),
info = tx.extractTimestamp(),
subtitle = stringReference(tx.getAmount(cryptoCurrency).trim()),
timestamp = tx.extractTimestamp(),
subtitleEndOffset = cryptoCurrency.symbol.length,
subtitleIconRes = tx.extractIconRes(),
)
}.insertWallets(walletsItem)
@ -104,8 +108,7 @@ internal class SendRecipientListConverter(
R.drawable.ic_arrow_down_24
}
private fun TxHistoryItem.getAmount(): String {
val cryptoCurrency = cryptoCurrencyStatusProvider().currency
private fun TxHistoryItem.getAmount(cryptoCurrency: CryptoCurrency): String {
return amount.toFormattedCurrencyString(
currency = cryptoCurrency.symbol,
decimals = cryptoCurrency.decimals,

View file

@ -50,7 +50,7 @@ internal fun SendNavigationButtons(uiState: SendUiState) {
private fun SendSecondaryNavigationButton(uiState: SendUiState) {
val currentState = uiState.currentState.collectAsState()
AnimatedVisibility(
visible = currentState.value == SendUiStateType.Recipient ||
visible = currentState.value == SendUiStateType.Amount ||
currentState.value == SendUiStateType.Fee,
) {
Icon(

View file

@ -38,16 +38,15 @@ internal fun SendScreen(uiState: SendUiState) {
horizontalAlignment = Alignment.CenterHorizontally,
) {
val titleRes = when (currentState.value) {
SendUiStateType.Amount -> R.string.common_send
SendUiStateType.Recipient -> R.string.send_recipient
SendUiStateType.Amount -> R.string.send_amount_label
SendUiStateType.Recipient -> R.string.send_recipient_label
SendUiStateType.Fee -> R.string.common_fee_selector_title
SendUiStateType.Send -> if (isSuccess?.value == false) R.string.common_send else null
SendUiStateType.Send -> if (isSuccess?.value == false) R.string.send_confirm_label else null
}
val iconRes = when (currentState.value) {
SendUiStateType.Amount,
SendUiStateType.Recipient,
-> R.drawable.ic_qrcode_scan_24
else -> null
val iconRes = if (currentState.value == SendUiStateType.Recipient) {
R.drawable.ic_qrcode_scan_24
} else {
null
}
AppBarWithBackButtonAndIcon(

View file

@ -3,24 +3,26 @@ package com.tangem.features.send.impl.presentation.ui.recipient
import androidx.annotation.DrawableRes
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import androidx.constraintlayout.compose.Visibility
import com.tangem.core.ui.components.MiddleEllipsisText
import com.tangem.core.ui.components.atoms.text.EllipsisText
import com.tangem.core.ui.components.atoms.text.TextEllipsis
import com.tangem.core.ui.components.icons.identicon.IdentIcon
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.send.impl.R
@ -33,6 +35,7 @@ import com.tangem.features.send.impl.R
* @param onClick click listener
* @param modifier modifier
* @param info info
* @param subtitleEndOffset offset for subtitle ellipsis
* @param subtitleIconRes icon
*/
@Suppress("DestructuringDeclarationWithTooManyEntries", "LongMethod")
@ -43,6 +46,7 @@ fun ListItemWithIcon(
onClick: () -> Unit,
modifier: Modifier = Modifier,
info: String? = null,
subtitleEndOffset: Int = 0,
@DrawableRes subtitleIconRes: Int? = null,
) {
ConstraintLayout(
@ -51,7 +55,7 @@ fun ListItemWithIcon(
.clickable { onClick() }
.padding(horizontal = TangemTheme.dimens.spacing12),
) {
val (iconRef, titleRef, subtitleRef, subtitleIconRef, infoRef) = createRefs()
val (iconRef, titleRef, subtitleRef, subtitleIconRef) = createRefs()
val spacing2 = TangemTheme.dimens.spacing2
val spacing8 = TangemTheme.dimens.spacing8
@ -68,11 +72,12 @@ fun ListItemWithIcon(
bottom.linkTo(parent.bottom, margin = spacing8)
},
)
MiddleEllipsisText(
EllipsisText(
text = title,
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Justify,
ellipsis = TextEllipsis.Middle,
modifier = Modifier
.constrainAs(titleRef) {
start.linkTo(iconRef.end, margin = spacing12)
@ -87,7 +92,7 @@ fun ListItemWithIcon(
tint = TangemTheme.colors.icon.informative,
modifier = Modifier
.size(TangemTheme.dimens.size16)
.background(TangemTheme.colors.background.tertiary, CircleShape)
.background(TangemTheme.colors.icon.informative.copy(alpha = 0.1f), CircleShape)
.constrainAs(subtitleIconRef) {
start.linkTo(iconRef.end, margin = spacing12)
top.linkTo(titleRef.bottom)
@ -95,36 +100,29 @@ fun ListItemWithIcon(
visibility = if (subtitleIconRes == null) Visibility.Gone else Visibility.Visible
},
)
Text(
text = subtitle,
val (text, offset) = remember(subtitle, info) {
if (info != null) {
val suffix = ", $info"
subtitle + suffix to suffix.length + subtitleEndOffset
} else {
subtitle to 0
}
}
EllipsisText(
text = text,
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
ellipsis = TextEllipsis.OffsetEnd(offsetEnd = offset),
modifier = Modifier
.constrainAs(subtitleRef) {
start.linkTo(subtitleIconRef.end, margin = spacing2, goneMargin = spacing12)
end.linkTo(infoRef.start)
end.linkTo(parent.end)
top.linkTo(titleRef.bottom)
bottom.linkTo(parent.bottom, margin = spacing10)
width = Dimension.fillToConstraints
},
)
info?.let {
Text(
text = it,
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
maxLines = 1,
modifier = Modifier
.constrainAs(infoRef) {
start.linkTo(subtitleRef.end, goneMargin = spacing12)
end.linkTo(parent.end)
top.linkTo(titleRef.bottom)
bottom.linkTo(parent.bottom, margin = spacing10)
},
)
}
}
}
@ -138,7 +136,7 @@ private fun ListItemWithIconPreview_Light(
ListItemWithIcon(
title = config.title,
subtitle = config.subtitle,
info = config.info,
subtitleEndOffset = config.subtitleEndOffset,
subtitleIconRes = config.iconRes,
onClick = {},
)
@ -154,7 +152,7 @@ private fun ListItemWithIconPreview_Dark(
ListItemWithIcon(
title = config.title,
subtitle = config.subtitle,
info = config.info,
subtitleEndOffset = config.subtitleEndOffset,
subtitleIconRes = config.iconRes,
onClick = {},
)
@ -165,6 +163,7 @@ private data class ListItemWithIconPreviewConfig(
val title: String,
val subtitle: String,
val info: String? = null,
val subtitleEndOffset: Int = 0,
val iconRes: Int? = null,
)
@ -174,12 +173,14 @@ private class ListItemWithIconPreviewProvider : CollectionPreviewParameterProvid
title = "0x34B4492A412D84A6E606288f3Bd714b89135D4dE",
subtitle = "0.000000000000000000000000000000 BTC",
info = "0.0.0000 at 00:00",
subtitleEndOffset = "BTC".length,
iconRes = R.drawable.ic_arrow_down_24,
),
ListItemWithIconPreviewConfig(
title = "0x34B4492A412D84A6E606288f3Bd714b89135D4dE",
subtitle = "1 BTC",
info = "0.0.0000 at 00:00",
subtitleEndOffset = "BTC".length,
iconRes = R.drawable.ic_arrow_down_24,
),
ListItemWithIconPreviewConfig(

View file

@ -141,7 +141,8 @@ private fun LazyListScope.recipientListItem(
ListItemWithIcon(
title = item.title.resolveReference(),
subtitle = item.subtitle.resolveReference(),
info = item.info?.let { ", ${it.resolveReference()}" },
info = item.timestamp?.resolveReference(),
subtitleEndOffset = item.subtitleEndOffset,
subtitleIconRes = item.subtitleIconRes,
modifier = Modifier
.then(