Updated on 2026-08-14
This commit is contained in:
parent
9c1931f7d7
commit
cfcc704666
21 changed files with 437 additions and 142 deletions
|
|
@ -194,13 +194,14 @@
|
|||
<string name="common_close">Close</string>
|
||||
<string name="exchange_send_view_header">You Send</string>
|
||||
<string name="exchange_receive_view_header">You Receive</string>
|
||||
<string name="swapping_generic_error">There was an error. Please try again.</string>
|
||||
<string name="swapping_permission_header">Give Permission</string>
|
||||
<string name="swapping_permission_subheader">To continue you need to allow 1inch smart contracts to use your %s</string>
|
||||
<string name="swapping_permission_rows_amount">Amount %s</string>
|
||||
<string name="swapping_permission_rows_your_wallet">Your Wallet</string>
|
||||
<string name="swapping_permission_rows_spender">Spender</string>
|
||||
<string name="swapping_permission_buttons_approve">Approve</string>
|
||||
<string name="swapping_swap_of_to">Swap of %s to</string>
|
||||
<string name="swapping_swap_of_to">Swap of %s to %s</string>
|
||||
<string name="swapping_swap">Swap</string>
|
||||
<string name="swapping_insufficient_funds">Insufficient funds</string>
|
||||
<string name="swapping_give_permission">Give Permission</string>
|
||||
|
|
|
|||
|
|
@ -194,13 +194,14 @@
|
|||
<string name="common_close">Close</string>
|
||||
<string name="exchange_send_view_header">You Send</string>
|
||||
<string name="exchange_receive_view_header">You Receive</string>
|
||||
<string name="swapping_generic_error">There was an error. Please try again.</string>
|
||||
<string name="swapping_permission_header">Give Permission</string>
|
||||
<string name="swapping_permission_subheader">To continue you need to allow 1inch smart contracts to use your %s</string>
|
||||
<string name="swapping_permission_rows_amount">Amount %s</string>
|
||||
<string name="swapping_permission_rows_your_wallet">Your Wallet</string>
|
||||
<string name="swapping_permission_rows_spender">Spender</string>
|
||||
<string name="swapping_permission_buttons_approve">Approve</string>
|
||||
<string name="swapping_swap_of_to">Swap of %s to</string>
|
||||
<string name="swapping_swap_of_to">Swap of %s to %s</string>
|
||||
<string name="swapping_swap">Swap</string>
|
||||
<string name="swapping_insufficient_funds">Insufficient funds</string>
|
||||
<string name="swapping_give_permission">Give Permission</string>
|
||||
|
|
|
|||
|
|
@ -194,13 +194,14 @@
|
|||
<string name="common_close">Close</string>
|
||||
<string name="exchange_send_view_header">You Send</string>
|
||||
<string name="exchange_receive_view_header">You Receive</string>
|
||||
<string name="swapping_generic_error">There was an error. Please try again.</string>
|
||||
<string name="swapping_permission_header">Give Permission</string>
|
||||
<string name="swapping_permission_subheader">To continue you need to allow 1inch smart contracts to use your %s</string>
|
||||
<string name="swapping_permission_rows_amount">Amount %s</string>
|
||||
<string name="swapping_permission_rows_your_wallet">Your Wallet</string>
|
||||
<string name="swapping_permission_rows_spender">Spender</string>
|
||||
<string name="swapping_permission_buttons_approve">Approve</string>
|
||||
<string name="swapping_swap_of_to">Swap of %s to</string>
|
||||
<string name="swapping_swap_of_to">Swap of %s to %s</string>
|
||||
<string name="swapping_swap">Swap</string>
|
||||
<string name="swapping_insufficient_funds">Insufficient funds</string>
|
||||
<string name="swapping_give_permission">Give Permission</string>
|
||||
|
|
|
|||
|
|
@ -194,13 +194,14 @@
|
|||
<string name="common_close">Закрыть</string>
|
||||
<string name="exchange_send_view_header">Вы отправляете</string>
|
||||
<string name="exchange_receive_view_header">Вы получаете</string>
|
||||
<string name="swapping_generic_error">Произошла ошибка. Пожалуйста, попробуйте еще раз.</string>
|
||||
<string name="swapping_permission_header">Дать разрешение</string>
|
||||
<string name="swapping_permission_subheader">Чтобы продолжить, вам нужно разрешить смарт-контракту 1inch использовать ваш %s</string>
|
||||
<string name="swapping_permission_rows_amount">Количество %s</string>
|
||||
<string name="swapping_permission_rows_your_wallet">Ваш кошелек</string>
|
||||
<string name="swapping_permission_rows_spender">Отправитель</string>
|
||||
<string name="swapping_permission_buttons_approve">Подтвердить</string>
|
||||
<string name="swapping_swap_of_to">Обмен %s на</string>
|
||||
<string name="swapping_swap_of_to">Обмен %s на %s</string>
|
||||
<string name="swapping_swap">Обмен</string>
|
||||
<string name="swapping_insufficient_funds">Недостаточно средств</string>
|
||||
<string name="swapping_give_permission">Дать разрешение</string>
|
||||
|
|
|
|||
|
|
@ -194,13 +194,14 @@
|
|||
<string name="common_close">Close</string>
|
||||
<string name="exchange_send_view_header">You Send</string>
|
||||
<string name="exchange_receive_view_header">You Receive</string>
|
||||
<string name="swapping_generic_error">There was an error. Please try again.</string>
|
||||
<string name="swapping_permission_header">Give Permission</string>
|
||||
<string name="swapping_permission_subheader">To continue you need to allow 1inch smart contracts to use your %s</string>
|
||||
<string name="swapping_permission_rows_amount">Amount %s</string>
|
||||
<string name="swapping_permission_rows_your_wallet">Your Wallet</string>
|
||||
<string name="swapping_permission_rows_spender">Spender</string>
|
||||
<string name="swapping_permission_buttons_approve">Approve</string>
|
||||
<string name="swapping_swap_of_to">Swap of %s to</string>
|
||||
<string name="swapping_swap_of_to">Swap of %s to %s</string>
|
||||
<string name="swapping_swap">Swap</string>
|
||||
<string name="swapping_insufficient_funds">Insufficient funds</string>
|
||||
<string name="swapping_give_permission">Give Permission</string>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,173 @@
|
|||
package com.tangem.core.ui.components
|
||||
|
||||
import androidx.compose.material.LocalTextStyle
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.SubcomposeLayout
|
||||
import androidx.compose.ui.text.TextLayoutResult
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.Constraints
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
|
||||
/**
|
||||
* https://stackoverflow.com/questions/69083061/how-to-make-middle-ellipsis-in-text-with-jetpack-compose
|
||||
*/
|
||||
@Composable
|
||||
fun MiddleEllipsisText(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = Color.Unspecified,
|
||||
fontSize: TextUnit = TextUnit.Unspecified,
|
||||
fontStyle: FontStyle? = null,
|
||||
fontWeight: FontWeight? = null,
|
||||
fontFamily: FontFamily? = null,
|
||||
letterSpacing: TextUnit = TextUnit.Unspecified,
|
||||
textDecoration: TextDecoration? = null,
|
||||
textAlign: TextAlign? = null,
|
||||
lineHeight: TextUnit = TextUnit.Unspecified,
|
||||
softWrap: Boolean = true,
|
||||
onTextLayout: (TextLayoutResult) -> Unit = {},
|
||||
style: TextStyle = LocalTextStyle.current,
|
||||
) {
|
||||
// some letters, like "r", will have less width when placed right before "."
|
||||
// adding a space to prevent such case
|
||||
val layoutText = remember(text) { "$text $ellipsisText" }
|
||||
val textLayoutResultState = remember(layoutText) {
|
||||
mutableStateOf<TextLayoutResult?>(null)
|
||||
}
|
||||
SubcomposeLayout(modifier) { constraints ->
|
||||
// result is ignored - we only need to fill our textLayoutResult
|
||||
subcompose("measure") {
|
||||
Text(
|
||||
text = layoutText,
|
||||
color = color,
|
||||
fontSize = fontSize,
|
||||
fontStyle = fontStyle,
|
||||
fontWeight = fontWeight,
|
||||
fontFamily = fontFamily,
|
||||
letterSpacing = letterSpacing,
|
||||
textDecoration = textDecoration,
|
||||
textAlign = textAlign,
|
||||
lineHeight = lineHeight,
|
||||
softWrap = softWrap,
|
||||
maxLines = 1,
|
||||
onTextLayout = { textLayoutResultState.value = it },
|
||||
style = style,
|
||||
)
|
||||
}.first().measure(Constraints())
|
||||
// to allow smart cast
|
||||
val textLayoutResult = textLayoutResultState.value
|
||||
?: // shouldn't happen - onTextLayout is called before subcompose finishes
|
||||
return@SubcomposeLayout layout(0, 0) {}
|
||||
val placeable = subcompose("visible") {
|
||||
val finalText = remember(text, textLayoutResult, constraints.maxWidth) {
|
||||
if (text.isEmpty() || textLayoutResult.getBoundingBox(text.indices.last).right <= constraints.maxWidth) {
|
||||
// text not including ellipsis fits on the first line.
|
||||
return@remember text
|
||||
}
|
||||
|
||||
val ellipsisWidth = layoutText.indices.toList()
|
||||
.takeLast(ellipsisCharactersCount)
|
||||
.let widthLet@{ indices ->
|
||||
// fix this bug: https://issuetracker.google.com/issues/197146630
|
||||
// in this case width is invalid
|
||||
for (i in indices) {
|
||||
val width = textLayoutResult.getBoundingBox(i).width
|
||||
if (width > 0) {
|
||||
return@widthLet width * ellipsisCharactersCount
|
||||
}
|
||||
}
|
||||
// this should not happen, because
|
||||
// this error occurs only for the last character in the string
|
||||
throw IllegalStateException("all ellipsis chars have invalid width")
|
||||
}
|
||||
val availableWidth = constraints.maxWidth - ellipsisWidth
|
||||
val startCounter = BoundCounter(text, textLayoutResult) { it }
|
||||
val endCounter = BoundCounter(text, textLayoutResult) { text.indices.last - it }
|
||||
|
||||
while (availableWidth - startCounter.width - endCounter.width > 0) {
|
||||
val possibleEndWidth = endCounter.widthWithNextChar()
|
||||
if (
|
||||
startCounter.width >= possibleEndWidth
|
||||
&& availableWidth - startCounter.width - possibleEndWidth >= 0
|
||||
) {
|
||||
endCounter.addNextChar()
|
||||
} else if (availableWidth - startCounter.widthWithNextChar() - endCounter.width >= 0) {
|
||||
startCounter.addNextChar()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
startCounter.string.trimEnd() + ellipsisText + endCounter.string.reversed().trimStart()
|
||||
}
|
||||
Text(
|
||||
text = finalText,
|
||||
color = color,
|
||||
fontSize = fontSize,
|
||||
fontStyle = fontStyle,
|
||||
fontWeight = fontWeight,
|
||||
fontFamily = fontFamily,
|
||||
letterSpacing = letterSpacing,
|
||||
textDecoration = textDecoration,
|
||||
textAlign = textAlign,
|
||||
lineHeight = lineHeight,
|
||||
softWrap = softWrap,
|
||||
onTextLayout = onTextLayout,
|
||||
style = style,
|
||||
)
|
||||
}[0].measure(constraints)
|
||||
layout(placeable.width, placeable.height) {
|
||||
placeable.place(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val ellipsisCharactersCount = 3
|
||||
private const val ellipsisCharacter = '.'
|
||||
private val ellipsisText = List(ellipsisCharactersCount) { ellipsisCharacter }.joinToString(separator = "")
|
||||
|
||||
private class BoundCounter(
|
||||
private val text: String,
|
||||
private val textLayoutResult: TextLayoutResult,
|
||||
private val charPosition: (Int) -> Int,
|
||||
) {
|
||||
var string = ""
|
||||
private set
|
||||
var width = 0f
|
||||
private set
|
||||
|
||||
private var _nextCharWidth: Float? = null
|
||||
private var invalidCharsCount = 0
|
||||
|
||||
fun widthWithNextChar(): Float =
|
||||
width + nextCharWidth()
|
||||
|
||||
private fun nextCharWidth(): Float =
|
||||
_nextCharWidth ?: run {
|
||||
var boundingBox: Rect
|
||||
// invalidCharsCount fixes this bug: https://issuetracker.google.com/issues/197146630
|
||||
invalidCharsCount--
|
||||
do {
|
||||
boundingBox = textLayoutResult
|
||||
.getBoundingBox(charPosition(string.count() + ++invalidCharsCount))
|
||||
} while (boundingBox.right == 0f)
|
||||
_nextCharWidth = boundingBox.width
|
||||
boundingBox.width
|
||||
}
|
||||
|
||||
fun addNextChar() {
|
||||
string += text[charPosition(string.count())]
|
||||
width += nextCharWidth()
|
||||
_nextCharWidth = null
|
||||
}
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.R
|
||||
|
|
@ -46,15 +47,15 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
*/
|
||||
@Composable
|
||||
fun ResultScreenContent(
|
||||
resultMessage: String,
|
||||
onButtonClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
resultMessage: AnnotatedString,
|
||||
@StringRes title: Int = R.string.common_success,
|
||||
resultColor: Color = TangemTheme.colors.icon.accent,
|
||||
@DrawableRes icon: Int = R.drawable.ic_check_24,
|
||||
@DrawableRes secondaryButtonIcon: Int? = null,
|
||||
@StringRes secondaryButtonText: Int? = null,
|
||||
onSecondaryButtonClick: (() -> Unit)? = null,
|
||||
onButtonClick: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
|
|
@ -92,6 +93,8 @@ fun ResultScreenContent(
|
|||
secondaryButtonText = secondaryButtonText,
|
||||
secondaryButtonIcon = secondaryButtonIcon,
|
||||
onSecondaryButtonClick = onSecondaryButtonClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
SpacerH12()
|
||||
}
|
||||
|
|
@ -140,22 +143,23 @@ fun SuccessImage(
|
|||
|
||||
@Composable
|
||||
private fun SecondaryButtonForResultScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
@StringRes secondaryButtonText: Int,
|
||||
onSecondaryButtonClick: () -> Unit,
|
||||
@DrawableRes secondaryButtonIcon: Int? = null,
|
||||
onSecondaryButtonClick: (() -> Unit),
|
||||
) {
|
||||
if (secondaryButtonIcon != null) {
|
||||
SecondaryButtonIconLeft(
|
||||
text = stringResource(id = secondaryButtonText),
|
||||
icon = painterResource(id = secondaryButtonIcon),
|
||||
onClick = onSecondaryButtonClick,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
SecondaryButton(
|
||||
text = stringResource(id = secondaryButtonText),
|
||||
onClick = onSecondaryButtonClick,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -165,7 +169,7 @@ private fun SecondaryButtonForResultScreen(
|
|||
@Composable
|
||||
private fun SuccessScreenPreview() {
|
||||
ResultScreenContent(
|
||||
resultMessage = "Swap of 1 000 DAI to 1 131,46 MATIC",
|
||||
resultMessage = AnnotatedString("Swap of 1 000 DAI to 1 131,46 MATIC"),
|
||||
secondaryButtonText = R.string.swapping_success_view_explorer_button_title,
|
||||
onSecondaryButtonClick = {},
|
||||
onButtonClick = {},
|
||||
|
|
@ -174,7 +178,7 @@ private fun SuccessScreenPreview() {
|
|||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_SuccessScreenContent_InLightTheme() {
|
||||
fun Preview_SuccessScreenContent_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
SuccessScreenPreview()
|
||||
}
|
||||
|
|
@ -182,7 +186,7 @@ private fun Preview_SuccessScreenContent_InLightTheme() {
|
|||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_SuccessScreenContent_InDarkTheme() {
|
||||
fun Preview_SuccessScreenContent_InDarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
SuccessScreenPreview()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
package com.tangem.core.ui.components
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
|
|
@ -25,11 +25,13 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
*/
|
||||
@Composable
|
||||
fun WarningCard(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
description: String,
|
||||
icon: @Composable (() -> Unit)? = null,
|
||||
) {
|
||||
WarningCardSurface(
|
||||
modifier = modifier,
|
||||
content = {
|
||||
WarningBody(
|
||||
title = title,
|
||||
|
|
@ -52,12 +54,14 @@ fun WarningCard(
|
|||
*/
|
||||
@Composable
|
||||
fun ClickableWarningCard(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
description: String,
|
||||
icon: @Composable (() -> Unit)? = null,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
WarningCardSurface(
|
||||
modifier = modifier,
|
||||
content = {
|
||||
WarningBody(title = title, description = description, icon = icon) {
|
||||
SpacerW12()
|
||||
|
|
@ -84,12 +88,14 @@ fun ClickableWarningCard(
|
|||
*/
|
||||
@Composable
|
||||
fun RefreshableWaringCard(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
description: String,
|
||||
icon: @Composable (() -> Unit)? = null,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
WarningCardSurface(
|
||||
modifier = modifier,
|
||||
content = {
|
||||
WarningBody(title = title, description = description, icon = icon) {
|
||||
SpacerW12()
|
||||
|
|
@ -108,6 +114,7 @@ fun RefreshableWaringCard(
|
|||
|
||||
@Composable
|
||||
private fun WarningBody(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
description: String,
|
||||
icon: @Composable (() -> Unit)? = null,
|
||||
|
|
@ -126,19 +133,20 @@ private fun WarningBody(
|
|||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun WarningCardSurface(
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: (() -> Unit)? = null,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Surface(
|
||||
Card(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.size12),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
modifier = Modifier.clickable(
|
||||
enabled = onClick != null,
|
||||
onClick = onClick ?: {},
|
||||
),
|
||||
onClick = { onClick ?: Unit },
|
||||
enabled = onClick != null,
|
||||
modifier = modifier,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
* @param expandedInitially whether the search is expanded on launch
|
||||
* @param tint tint for most of the visual elements of toolbar
|
||||
* @param onBackClick action when close button is clicked
|
||||
* @param onSearchChange action when search is modified
|
||||
* @param onSearchDisplayClose action when search is closed
|
||||
* @param onSearchChanged action when search is modified
|
||||
* @param onSearchDisplayClosed action when search is closed
|
||||
*
|
||||
* @see <a href =
|
||||
* "https://www.figma.com/file/Vs6SkVsFnUPsSCNwlnVf5U/Android-%E2%80%93-UI?node-id=1123%3A4068&t=xj8BBj5DfCWn2Mli-1"
|
||||
|
|
@ -54,13 +54,14 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
*/
|
||||
@Composable
|
||||
fun ExpandableSearchView(
|
||||
onBackClick: () -> Unit,
|
||||
onSearchChange: (String) -> Unit,
|
||||
onSearchDisplayClose: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
title: String? = null,
|
||||
placeholderSearchText: String = "",
|
||||
expandedInitially: Boolean = false,
|
||||
tint: Color = TangemTheme.colors.text.primary1,
|
||||
tint: Color = TangemTheme.colors.icon.primary1,
|
||||
onBackClick: () -> Unit,
|
||||
onSearchChanged: (String) -> Unit,
|
||||
onSearchDisplayClosed: () -> Unit,
|
||||
) {
|
||||
val (expanded, onExpandedChanged) = remember {
|
||||
mutableStateOf(expandedInitially)
|
||||
|
|
@ -70,16 +71,18 @@ fun ExpandableSearchView(
|
|||
if (isSearchFieldVisible) {
|
||||
ExpandedSearchView(
|
||||
placeholderSearchText = placeholderSearchText,
|
||||
onSearchChange = onSearchChange,
|
||||
onSearchDisplayClose = onSearchDisplayClose,
|
||||
onExpandedChange = onExpandedChanged,
|
||||
onSearchChanged = onSearchChanged,
|
||||
onSearchDisplayClosed = onSearchDisplayClosed,
|
||||
onExpandedChanged = onExpandedChanged,
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
} else {
|
||||
CollapsedSearchView(
|
||||
title = title,
|
||||
onBackClick = onBackClick,
|
||||
onExpandedChange = onExpandedChanged,
|
||||
onExpandedChanged = onExpandedChanged,
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
}
|
||||
|
|
@ -88,14 +91,15 @@ fun ExpandableSearchView(
|
|||
|
||||
@Composable
|
||||
private fun CollapsedSearchView(
|
||||
onBackClick: () -> Unit,
|
||||
onExpandedChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
title: String? = null,
|
||||
tint: Color = TangemTheme.colors.background.primary,
|
||||
onBackClick: () -> Unit,
|
||||
onExpandedChanged: (Boolean) -> Unit,
|
||||
tint: Color,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.secondary)
|
||||
.padding(TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16),
|
||||
|
|
@ -123,7 +127,7 @@ private fun CollapsedSearchView(
|
|||
tint = tint,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.clickable { onExpandedChange(true) },
|
||||
.clickable { onExpandedChanged(true) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -131,10 +135,11 @@ private fun CollapsedSearchView(
|
|||
@Composable
|
||||
private fun ExpandedSearchView(
|
||||
placeholderSearchText: String,
|
||||
onSearchChange: (String) -> Unit,
|
||||
onSearchDisplayClose: () -> Unit,
|
||||
onExpandedChange: (Boolean) -> Unit,
|
||||
tint: Color = TangemTheme.colors.background.primary,
|
||||
onSearchChanged: (String) -> Unit,
|
||||
onSearchDisplayClosed: () -> Unit,
|
||||
onExpandedChanged: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
tint: Color,
|
||||
) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
val textFieldFocusRequester = remember { FocusRequester() }
|
||||
|
|
@ -146,16 +151,16 @@ private fun ExpandedSearchView(
|
|||
var textFieldValue by remember { mutableStateOf(TextFieldValue("", TextRange("".length))) }
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.secondary)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
onExpandedChange(false)
|
||||
onSearchDisplayClose()
|
||||
onExpandedChanged(false)
|
||||
onSearchDisplayClosed()
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
|
|
@ -168,7 +173,7 @@ private fun ExpandedSearchView(
|
|||
value = textFieldValue,
|
||||
onValueChange = {
|
||||
textFieldValue = it
|
||||
onSearchChange(it.text)
|
||||
onSearchChanged(it.text)
|
||||
},
|
||||
singleLine = true,
|
||||
modifier = Modifier
|
||||
|
|
@ -200,8 +205,8 @@ private fun CollapsedSearchViewPreview() {
|
|||
title = "Choose Token",
|
||||
onBackClick = {},
|
||||
placeholderSearchText = "Search",
|
||||
onSearchChange = {},
|
||||
onSearchDisplayClose = {},
|
||||
onSearchChanged = {},
|
||||
onSearchDisplayClosed = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -214,9 +219,9 @@ private fun ExpandedSearchViewPreview() {
|
|||
title = "Choose Token",
|
||||
onBackClick = {},
|
||||
placeholderSearchText = "Search",
|
||||
onSearchChange = {},
|
||||
onSearchChanged = {},
|
||||
expandedInitially = true,
|
||||
onSearchDisplayClose = {},
|
||||
onSearchDisplayClosed = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ dependencies {
|
|||
implementation(AndroidX.appCompat)
|
||||
implementation(AndroidX.fragmentKtx)
|
||||
implementation(AndroidX.lifecycleViewModelKtx)
|
||||
implementation(AndroidX.browser)
|
||||
|
||||
/** Compose */
|
||||
implementation(Compose.foundation)
|
||||
|
|
|
|||
|
|
@ -67,7 +67,8 @@ sealed interface TransactionCardType {
|
|||
|
||||
sealed interface SwapWarning {
|
||||
data class PermissionNeeded(val tokenCurrency: String) : SwapWarning
|
||||
data class GenericWarning(val message: String, val onClick: () -> Unit) : SwapWarning
|
||||
data class InsufficientFunds(val tokenCurrency: String) : SwapWarning
|
||||
data class GenericWarning(val message: String?, val onClick: () -> Unit) : SwapWarning
|
||||
// data class RateExpired(val onClick: () -> Unit) : SwapWarning
|
||||
// object HighPriceImpact : SwapWarning
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.feature.swap.models
|
||||
|
||||
data class SwapSuccessStateHolder(
|
||||
val message: String,
|
||||
val fromTokenAmount: String,
|
||||
val toTokenAmount: String,
|
||||
val onSecondaryButtonClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -4,11 +4,14 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.tangem.feature.swap.router.CustomTabsManager
|
||||
import com.tangem.feature.swap.router.SwapRouter
|
||||
import com.tangem.feature.swap.router.SwapScreen
|
||||
import com.tangem.feature.swap.ui.SwapScreen
|
||||
|
|
@ -25,35 +28,53 @@ class SwapFragment : Fragment() {
|
|||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
activity?.window?.let { WindowCompat.setDecorFitsSystemWindows(it, true) }
|
||||
viewModel.setRouter(SwapRouter(fragmentManager = WeakReference(parentFragmentManager)))
|
||||
viewModel.setRouter(
|
||||
SwapRouter(
|
||||
fragmentManager = WeakReference(parentFragmentManager),
|
||||
customTabsManager = CustomTabsManager(WeakReference(activity?.application)),
|
||||
),
|
||||
)
|
||||
|
||||
activity?.onBackPressedDispatcher?.addCallback(
|
||||
viewLifecycleOwner,
|
||||
object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
viewModel.uiState.onBackClicked()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return ComposeView(inflater.context).apply {
|
||||
setContent {
|
||||
Crossfade(targetState = viewModel.currentScreen) { screen ->
|
||||
when (screen) {
|
||||
SwapScreen.Main -> SwapScreen(stateHolder = viewModel.uiState)
|
||||
SwapScreen.Success -> {
|
||||
val successState = viewModel.uiState.successState
|
||||
if (successState != null) {
|
||||
SwapSuccessScreen(state = successState, viewModel.uiState.onBackClicked)
|
||||
} else {
|
||||
SwapScreen(stateHolder = viewModel.uiState)
|
||||
}
|
||||
}
|
||||
SwapScreen.SelectToken -> {
|
||||
val tokenState = viewModel.uiState.selectTokenState
|
||||
if (tokenState != null) {
|
||||
SwapSelectTokenScreen(
|
||||
state = tokenState,
|
||||
onBack = viewModel.uiState.onBackClicked,
|
||||
)
|
||||
} else {
|
||||
SwapScreen(stateHolder = viewModel.uiState)
|
||||
}
|
||||
}
|
||||
// screen
|
||||
ScreenContent(viewModel = viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScreenContent(viewModel: SwapViewModel) {
|
||||
Crossfade(targetState = viewModel.currentScreen) { screen ->
|
||||
when (screen) {
|
||||
SwapScreen.Main -> SwapScreen(stateHolder = viewModel.uiState)
|
||||
SwapScreen.Success -> {
|
||||
val successState = viewModel.uiState.successState
|
||||
if (successState != null) {
|
||||
SwapSuccessScreen(state = successState, viewModel.uiState.onBackClicked)
|
||||
} else {
|
||||
SwapScreen(stateHolder = viewModel.uiState)
|
||||
}
|
||||
}
|
||||
SwapScreen.SelectToken -> {
|
||||
val tokenState = viewModel.uiState.selectTokenState
|
||||
if (tokenState != null) {
|
||||
SwapSelectTokenScreen(
|
||||
state = tokenState,
|
||||
onBack = viewModel.uiState.onBackClicked,
|
||||
)
|
||||
} else {
|
||||
SwapScreen(stateHolder = viewModel.uiState)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.feature.swap.router
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.browser.customtabs.CustomTabColorSchemeParams
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class CustomTabsManager(private val context: WeakReference<Context>) {
|
||||
fun openUrl(url: String) {
|
||||
val customTabsIntent = CustomTabsIntent.Builder()
|
||||
.setDefaultColorSchemeParams(
|
||||
CustomTabColorSchemeParams.Builder()
|
||||
.build(),
|
||||
)
|
||||
.build()
|
||||
context.get()?.let { customTabsIntent.launchUrl(it, Uri.parse(url)) }
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import java.lang.ref.WeakReference
|
|||
|
||||
internal class SwapRouter(
|
||||
private val fragmentManager: WeakReference<FragmentManager>,
|
||||
private val customTabsManager: CustomTabsManager,
|
||||
) {
|
||||
|
||||
var currentScreen by mutableStateOf(SwapScreen.Main)
|
||||
|
|
@ -24,6 +25,10 @@ internal class SwapRouter(
|
|||
fragmentManager.get()?.popBackStack()
|
||||
}
|
||||
}
|
||||
|
||||
fun openUrl(url: String) {
|
||||
customTabsManager.openUrl(url)
|
||||
}
|
||||
}
|
||||
|
||||
enum class SwapScreen {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import androidx.compose.ui.res.painterResource
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.MiddleEllipsisText
|
||||
import com.tangem.core.ui.components.PrimaryButtonIconRight
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.components.SpacerH10
|
||||
|
|
@ -151,11 +152,11 @@ private fun InformationItem(subtitle: String, value: String) {
|
|||
|
||||
SpacerH16()
|
||||
|
||||
Text(
|
||||
MiddleEllipsisText(
|
||||
text = value,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.body2,
|
||||
maxLines = 1,
|
||||
modifier = Modifier.padding(start = TangemTheme.dimens.spacing16),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.swap.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.BottomSheetScaffold
|
||||
import androidx.compose.material.BottomSheetState
|
||||
|
|
@ -8,6 +9,7 @@ import androidx.compose.material.ExperimentalMaterialApi
|
|||
import androidx.compose.material.rememberBottomSheetScaffoldState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -26,6 +28,7 @@ internal fun SwapScreen(stateHolder: SwapStateHolder) {
|
|||
|
||||
TangemTheme {
|
||||
BottomSheetScaffold(
|
||||
modifier = Modifier.background(TangemTheme.colors.background.secondary),
|
||||
topBar = {
|
||||
AppBarWithBackButton(
|
||||
text = stringResource(R.string.swapping_swap),
|
||||
|
|
@ -34,8 +37,8 @@ internal fun SwapScreen(stateHolder: SwapStateHolder) {
|
|||
)
|
||||
},
|
||||
sheetContent = {
|
||||
SwapPermissionBottomSheetContent(
|
||||
data = stateHolder.permissionState as? SwapPermissionState.ReadyForRequest,
|
||||
SwapPermissionBottomSheetContent(
|
||||
data = stateHolder.permissionState as? SwapPermissionState.ReadyForRequest,
|
||||
onCancel = { coroutineScope.launch { bottomSheetScaffoldState.bottomSheetState.collapse() } },
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -18,13 +18,11 @@ import androidx.compose.material.Card
|
|||
import androidx.compose.material.CircularProgressIndicator
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
|
|
@ -32,6 +30,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import com.tangem.core.ui.components.CardWithIcon
|
||||
import com.tangem.core.ui.components.ClickableWarningCard
|
||||
import com.tangem.core.ui.components.Keyboard
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.PrimaryButtonIconRight
|
||||
import com.tangem.core.ui.components.RefreshableWaringCard
|
||||
import com.tangem.core.ui.components.SimpleOkDialog
|
||||
|
|
@ -57,13 +56,12 @@ internal fun SwapScreenContent(state: SwapStateHolder, onPermissionWarningClick:
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(color = Color.Transparent),
|
||||
.background(color = TangemTheme.colors.background.secondary),
|
||||
) {
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(color = TangemTheme.colors.background.primary)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
|
|
@ -101,14 +99,24 @@ internal fun SwapScreenContent(state: SwapStateHolder, onPermissionWarningClick:
|
|||
)
|
||||
}
|
||||
|
||||
PrimaryButtonIconRight(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.swapping_swap),
|
||||
icon = painterResource(id = R.drawable.ic_tangem_24),
|
||||
enabled = state.swapButton.enabled,
|
||||
showProgress = state.swapButton.loading,
|
||||
onClick = state.swapButton.onClick,
|
||||
)
|
||||
if (state.warnings.any { it is SwapWarning.InsufficientFunds }) {
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.swapping_insufficient_funds),
|
||||
enabled = false,
|
||||
showProgress = state.swapButton.loading,
|
||||
onClick = state.swapButton.onClick,
|
||||
)
|
||||
} else {
|
||||
PrimaryButtonIconRight(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.swapping_swap),
|
||||
icon = painterResource(id = R.drawable.ic_tangem_24),
|
||||
enabled = state.swapButton.enabled,
|
||||
showProgress = state.swapButton.loading,
|
||||
onClick = state.swapButton.onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
|
|
@ -134,7 +142,10 @@ internal fun SwapScreenContent(state: SwapStateHolder, onPermissionWarningClick:
|
|||
}
|
||||
|
||||
if (state.alert != null) {
|
||||
SimpleOkDialog(message = state.alert.message, onDismissDialog = state.alert.onClick)
|
||||
SimpleOkDialog(
|
||||
message = state.alert.message ?: stringResource(id = R.string.swapping_generic_error),
|
||||
onDismissDialog = state.alert.onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -222,7 +233,7 @@ private fun SwapWarnings(
|
|||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(color = MaterialTheme.colors.primary)
|
||||
.background(color = TangemTheme.colors.background.secondary)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
warnings.forEach { warning ->
|
||||
|
|
@ -250,10 +261,11 @@ private fun SwapWarnings(
|
|||
is SwapWarning.GenericWarning -> {
|
||||
RefreshableWaringCard(
|
||||
title = stringResource(id = R.string.common_warning),
|
||||
description = warning.message,
|
||||
description = warning.message ?: stringResource(id = R.string.swapping_generic_error),
|
||||
onClick = warning.onClick,
|
||||
)
|
||||
}
|
||||
else -> {}
|
||||
// is SwapWarning.RateExpired -> {
|
||||
// RefreshableWaringCard(
|
||||
// title = stringResource(id = R.string.),
|
||||
|
|
|
|||
|
|
@ -49,10 +49,11 @@ fun SwapSelectTokenScreen(state: SwapSelectTokenStateHolder, onBack: () -> Unit)
|
|||
title = stringResource(R.string.swapping_token_list_your_title),
|
||||
onBackClick = onBack,
|
||||
placeholderSearchText = stringResource(id = R.string.search_tokens_title),
|
||||
onSearchChange = state.onSearchEntered,
|
||||
onSearchDisplayClose = { state.onSearchEntered("") },
|
||||
onSearchChanged = state.onSearchEntered,
|
||||
onSearchDisplayClosed = { state.onSearchEntered("") },
|
||||
)
|
||||
},
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.secondary),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -61,12 +62,8 @@ fun SwapSelectTokenScreen(state: SwapSelectTokenStateHolder, onBack: () -> Unit)
|
|||
private fun ListOfTokens(state: SwapSelectTokenStateHolder, modifier: Modifier = Modifier) {
|
||||
LazyColumn(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.background(color = TangemTheme.colors.background.primary)
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing32,
|
||||
),
|
||||
.background(color = TangemTheme.colors.background.secondary)
|
||||
.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
|
||||
) {
|
||||
|
|
@ -85,16 +82,17 @@ private fun ListOfTokens(state: SwapSelectTokenStateHolder, modifier: Modifier =
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenItem(token: TokenToSelect, onTokenClick: () -> Unit) {
|
||||
private fun TokenItem(token: TokenToSelect, onTokenClick: (String) -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
top = TangemTheme.dimens.spacing14,
|
||||
bottom = TangemTheme.dimens.spacing14,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
.clickable(
|
||||
onClick = { onTokenClick(token.id) },
|
||||
)
|
||||
.clickable(onClick = onTokenClick),
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens.spacing14,
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
TokenIcon(token = token)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ import androidx.compose.material.Scaffold
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.ResultScreenContent
|
||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
||||
|
|
@ -18,7 +21,10 @@ fun SwapSuccessScreen(state: SwapSuccessStateHolder, onBack: () -> Unit) {
|
|||
Scaffold(
|
||||
content = { padding ->
|
||||
ResultScreenContent(
|
||||
resultMessage = state.message,
|
||||
resultMessage = makeSuccessMessage(
|
||||
fromTokenAmount = state.fromTokenAmount,
|
||||
toTokenAmount = state.toTokenAmount,
|
||||
),
|
||||
resultColor = TangemTheme.colors.icon.attention,
|
||||
onButtonClick = onBack,
|
||||
icon = R.drawable.ic_clock_24,
|
||||
|
|
@ -40,10 +46,24 @@ fun SwapSuccessScreen(state: SwapSuccessStateHolder, onBack: () -> Unit) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun makeSuccessMessage(fromTokenAmount: String, toTokenAmount: String): AnnotatedString {
|
||||
val message = stringResource(id = R.string.swapping_swap_of_to, fromTokenAmount, toTokenAmount)
|
||||
return buildAnnotatedString {
|
||||
append(message)
|
||||
addStyle(
|
||||
style = SpanStyle(color = TangemTheme.colors.text.accent),
|
||||
start = message.indexOf(toTokenAmount),
|
||||
end = message.length,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region preview
|
||||
|
||||
private val state = SwapSuccessStateHolder(
|
||||
message = "Swap of 1 000 DAI to 1 131,46 MATIC",
|
||||
fromTokenAmount = "1 000 DAI",
|
||||
toTokenAmount = "1 131,46 MATIC",
|
||||
) {}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import androidx.annotation.DrawableRes
|
|||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
|
|
@ -23,17 +24,22 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
||||
import androidx.compose.foundation.text.selection.TextSelectionColors
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Color.Companion.Transparent
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
|
|
@ -52,7 +58,6 @@ import androidx.compose.ui.unit.sp
|
|||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.CurrencyPlaceholderIcon
|
||||
import com.tangem.core.ui.components.FontSizeRange
|
||||
import com.tangem.core.ui.components.ResizableText
|
||||
import com.tangem.core.ui.components.SpacerH4
|
||||
|
|
@ -63,6 +68,7 @@ import com.valentinilk.shimmer.shimmer
|
|||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
fun TransactionCard(
|
||||
modifier: Modifier = Modifier,
|
||||
type: TransactionCardType,
|
||||
balance: String,
|
||||
amount: String?,
|
||||
|
|
@ -73,6 +79,7 @@ fun TransactionCard(
|
|||
onChangeTokenClick: (() -> Unit)? = null,
|
||||
) {
|
||||
Card(
|
||||
modifier = modifier,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius12),
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
|
|
@ -112,7 +119,10 @@ fun TransactionCard(
|
|||
.align(Alignment.CenterEnd)
|
||||
.height(TangemTheme.dimens.size116)
|
||||
.width(TangemTheme.dimens.size102)
|
||||
.clickable { onChangeTokenClick() },
|
||||
.clickable(
|
||||
indication = rememberRipple(bounded = false),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
) { onChangeTokenClick() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -209,7 +219,7 @@ private fun Content(
|
|||
is TransactionCardType.SendCard -> {
|
||||
AutoSizeTextField(
|
||||
amount = amount ?: "1",
|
||||
onAmountChange = { type.onAmountChanged(it) },
|
||||
onAmoutChanged = { type.onAmountChanged(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -245,7 +255,7 @@ private fun Content(
|
|||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
private fun AutoSizeTextField(amount: String, onAmountChange: (String) -> Unit) {
|
||||
private fun AutoSizeTextField(amount: String, onAmoutChanged: (String) -> Unit) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
val textFieldFocusRequester = remember { FocusRequester() }
|
||||
|
||||
|
|
@ -272,31 +282,39 @@ private fun AutoSizeTextField(amount: String, onAmountChange: (String) -> Unit)
|
|||
intrinsics = calculateIntrinsics()
|
||||
}
|
||||
}
|
||||
|
||||
BasicTextField(
|
||||
value = amount,
|
||||
onValueChange = {
|
||||
onAmountChange(it)
|
||||
},
|
||||
singleLine = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(textFieldFocusRequester),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Done,
|
||||
keyboardType = KeyboardType.Decimal,
|
||||
),
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = {
|
||||
focusManager.clearFocus()
|
||||
},
|
||||
),
|
||||
textStyle = TangemTheme.typography.h2.copy(
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
fontSize = shrunkFontSize,
|
||||
),
|
||||
cursorBrush = SolidColor(TangemTheme.colors.text.primary1),
|
||||
val customTextSelectionColors = TextSelectionColors(
|
||||
handleColor = Transparent,
|
||||
backgroundColor = TangemTheme.colors.text.secondary.copy(alpha = 0.4f),
|
||||
)
|
||||
CompositionLocalProvider(
|
||||
LocalTextSelectionColors provides customTextSelectionColors,
|
||||
) {
|
||||
|
||||
BasicTextField(
|
||||
value = amount,
|
||||
onValueChange = {
|
||||
onAmoutChanged(it)
|
||||
},
|
||||
singleLine = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(textFieldFocusRequester),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Done,
|
||||
keyboardType = KeyboardType.Decimal,
|
||||
),
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = {
|
||||
focusManager.clearFocus()
|
||||
},
|
||||
),
|
||||
textStyle = TangemTheme.typography.h2.copy(
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
fontSize = shrunkFontSize,
|
||||
),
|
||||
cursorBrush = SolidColor(TangemTheme.colors.text.primary1),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -333,8 +351,8 @@ fun Token(
|
|||
.crossfade(true)
|
||||
.build(),
|
||||
loading = { TokenImageShimmer(modifier = tokenImageModifier) },
|
||||
error = { CurrencyPlaceholderIcon(modifier = tokenImageModifier, id = tokenCurrency) },
|
||||
contentDescription = null,
|
||||
// error = { CurrencyPlaceholderIcon(modifier = tokenImageModifier, tokenCurrency) },
|
||||
contentDescription = tokenCurrency,
|
||||
)
|
||||
|
||||
if (networkIconRes != null) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue