Updated on 2026-08-14
This commit is contained in:
commit
615fb8d718
16 changed files with 572 additions and 22 deletions
|
|
@ -204,6 +204,9 @@
|
|||
<string name="swapping_insufficient_funds">Insufficient funds</string>
|
||||
<string name="swapping_give_permission">Give Permission</string>
|
||||
<string name="swapping_permit_and_swap">Permit and Swap</string>
|
||||
<string name="swapping_token_list_your_title">Choose token</string>
|
||||
<string name="swapping_token_list_your_tokens">Your tokens</string>
|
||||
<string name="swapping_token_list_other_tokens">Other tokens</string>
|
||||
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
|
||||
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Reason: %s. Please try again later.</string>
|
||||
<string name="referral_error_failed_to_participate">Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support.</string>
|
||||
|
|
|
|||
|
|
@ -204,6 +204,9 @@
|
|||
<string name="swapping_insufficient_funds">Insufficient funds</string>
|
||||
<string name="swapping_give_permission">Give Permission</string>
|
||||
<string name="swapping_permit_and_swap">Permit and Swap</string>
|
||||
<string name="swapping_token_list_your_title">Choose token</string>
|
||||
<string name="swapping_token_list_your_tokens">Your tokens</string>
|
||||
<string name="swapping_token_list_other_tokens">Other tokens</string>
|
||||
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
|
||||
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Reason: %s. Please try again later.</string>
|
||||
<string name="referral_error_failed_to_participate">Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support.</string>
|
||||
|
|
|
|||
|
|
@ -204,6 +204,9 @@
|
|||
<string name="swapping_insufficient_funds">Insufficient funds</string>
|
||||
<string name="swapping_give_permission">Give Permission</string>
|
||||
<string name="swapping_permit_and_swap">Permit and Swap</string>
|
||||
<string name="swapping_token_list_your_title">Choose token</string>
|
||||
<string name="swapping_token_list_your_tokens">Your tokens</string>
|
||||
<string name="swapping_token_list_other_tokens">Other tokens</string>
|
||||
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
|
||||
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Reason: %s. Please try again later.</string>
|
||||
<string name="referral_error_failed_to_participate">Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support.</string>
|
||||
|
|
|
|||
|
|
@ -204,6 +204,9 @@
|
|||
<string name="swapping_insufficient_funds">Недостаточно средств</string>
|
||||
<string name="swapping_give_permission">Дать разрешение</string>
|
||||
<string name="swapping_permit_and_swap">Разрешить и обменять</string>
|
||||
<string name="swapping_token_list_your_title">Выберите токен</string>
|
||||
<string name="swapping_token_list_your_tokens">Ваши токены</string>
|
||||
<string name="swapping_token_list_other_tokens">Другие токены</string>
|
||||
<string name="referral_error_failed_to_load_info">Не удалось загрузить информацию по реферальной программе. Пожалуйста, попробуйте позже.</string>
|
||||
<string name="referral_error_failed_to_load_info_with_reason">Не удалось загрузить информацию по реферальной программе. Причина: %s. Пожалуйста, попробуйте позже.</string>
|
||||
<string name="referral_error_failed_to_participate">Не удалось обработать вашу заявку на участие. Причина: %s. Пожалуйста, попробуйте позже. Если проблема сохранится, вы можете обратиться в техподдержку.</string>
|
||||
|
|
|
|||
|
|
@ -204,6 +204,9 @@
|
|||
<string name="swapping_insufficient_funds">Insufficient funds</string>
|
||||
<string name="swapping_give_permission">Give Permission</string>
|
||||
<string name="swapping_permit_and_swap">Permit and Swap</string>
|
||||
<string name="swapping_token_list_your_title">Choose token</string>
|
||||
<string name="swapping_token_list_your_tokens">Your tokens</string>
|
||||
<string name="swapping_token_list_other_tokens">Other tokens</string>
|
||||
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
|
||||
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Reason: %s. Please try again later.</string>
|
||||
<string name="referral_error_failed_to_participate">Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support.</string>
|
||||
|
|
|
|||
|
|
@ -100,6 +100,11 @@ fun SpacerW(width: Dp, modifier: Modifier = Modifier) {
|
|||
Spacer(modifier = modifier.width(width))
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerW2(modifier: Modifier = Modifier) {
|
||||
SpacerW(2.dp, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpacerW4(modifier: Modifier = Modifier) {
|
||||
SpacerW(4.dp, modifier)
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ private fun TangemTextFieldSize.toShape(): Shape = when (this) {
|
|||
TangemTextFieldSize.Default -> TangemTheme.shapes.roundedCornersSmall2
|
||||
}
|
||||
|
||||
private object TangemTextFieldsDefault {
|
||||
internal object TangemTextFieldsDefault {
|
||||
val defaultTextFieldColors: TangemTextFieldColors
|
||||
@Composable @Stable get() = TangemTextFieldColors(
|
||||
textColor = TangemTheme.colors.text.primary1,
|
||||
|
|
@ -223,7 +223,7 @@ private object TangemTextFieldsDefault {
|
|||
}
|
||||
|
||||
@Immutable
|
||||
private data class TangemTextFieldColors(
|
||||
internal data class TangemTextFieldColors(
|
||||
private val textColor: Color,
|
||||
private val disabledTextColor: Color,
|
||||
private val cursorColor: Color,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,245 @@
|
|||
package com.tangem.core.ui.components.appbar
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
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.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.components.TangemTextFieldsDefault
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
/**
|
||||
* App bar with close icon and search functionality
|
||||
*
|
||||
* @param title optional title
|
||||
* @param placeholderSearchText optional text placeholder for search TextField
|
||||
* @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 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"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun ExpandableSearchView(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String? = null,
|
||||
placeholderSearchText: String = "",
|
||||
expandedInitially: Boolean = false,
|
||||
tint: Color = TangemTheme.colors.text.primary1,
|
||||
onBackClick: () -> Unit,
|
||||
onSearchChanged: (String) -> Unit,
|
||||
onSearchDisplayClosed: () -> Unit,
|
||||
) {
|
||||
val (expanded, onExpandedChanged) = remember {
|
||||
mutableStateOf(expandedInitially)
|
||||
}
|
||||
|
||||
|
||||
Crossfade(targetState = expanded) { isSearchFieldVisible ->
|
||||
if (isSearchFieldVisible) {
|
||||
ExpandedSearchView(
|
||||
placeholderSearchText = placeholderSearchText,
|
||||
onSearchChanged = onSearchChanged,
|
||||
onSearchDisplayClosed = onSearchDisplayClosed,
|
||||
onExpandedChanged = onExpandedChanged,
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
} else {
|
||||
CollapsedSearchView(
|
||||
title = title,
|
||||
onBackClick = onBackClick,
|
||||
onExpandedChanged = onExpandedChanged,
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SearchIcon(iconTint: Color) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_search_24),
|
||||
contentDescription = null,
|
||||
tint = iconTint,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CollapsedSearchView(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String? = null,
|
||||
onBackClick: () -> Unit,
|
||||
onExpandedChanged: (Boolean) -> Unit,
|
||||
tint: Color = TangemTheme.colors.background.primary,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.padding(TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_close_24),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size24)
|
||||
.clickable { onBackClick() },
|
||||
tint = MaterialTheme.colors.onPrimary,
|
||||
)
|
||||
if (!title.isNullOrBlank()) {
|
||||
Text(
|
||||
text = title,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
)
|
||||
}
|
||||
SpacerWMax()
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_search_24),
|
||||
tint = tint,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.clickable { onExpandedChanged(true) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExpandedSearchView(
|
||||
placeholderSearchText: String,
|
||||
onSearchChanged: (String) -> Unit,
|
||||
onSearchDisplayClosed: () -> Unit,
|
||||
onExpandedChanged: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
tint: Color = TangemTheme.colors.background.primary,
|
||||
) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
val textFieldFocusRequester = remember { FocusRequester() }
|
||||
|
||||
SideEffect {
|
||||
textFieldFocusRequester.requestFocus()
|
||||
}
|
||||
|
||||
var textFieldValue by remember {
|
||||
mutableStateOf(TextFieldValue("", TextRange("".length)))
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
onExpandedChanged(false)
|
||||
onSearchDisplayClosed()
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_back_24),
|
||||
contentDescription = null,
|
||||
tint = tint,
|
||||
)
|
||||
}
|
||||
TextField(
|
||||
value = textFieldValue,
|
||||
onValueChange = {
|
||||
textFieldValue = it
|
||||
onSearchChanged(it.text)
|
||||
},
|
||||
singleLine = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(textFieldFocusRequester),
|
||||
placeholder = {
|
||||
Text(text = placeholderSearchText)
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Done,
|
||||
),
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = {
|
||||
focusManager.clearFocus()
|
||||
},
|
||||
),
|
||||
colors = TangemTextFieldsDefault.defaultTextFieldColors
|
||||
.copy(
|
||||
placeholderColor = TangemTheme.colors.text.disabled,
|
||||
cursorColor = TangemTheme.colors.text.tertiary,
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
disabledIndicatorColor = Color.Transparent,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun CollapsedSearchViewPreview() {
|
||||
TangemTheme {
|
||||
ExpandableSearchView(
|
||||
title = "Choose Token",
|
||||
onBackClick = {},
|
||||
placeholderSearchText = "Search",
|
||||
onSearchChanged = {},
|
||||
onSearchDisplayClosed = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun ExpandedSearchViewPreview() {
|
||||
TangemTheme {
|
||||
ExpandableSearchView(
|
||||
title = "Choose Token",
|
||||
onBackClick = {},
|
||||
placeholderSearchText = "Search",
|
||||
onSearchChanged = {},
|
||||
expandedInitially = true,
|
||||
onSearchDisplayClosed = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,6 +65,7 @@ data class TangemDimens internal constructor(
|
|||
val spacing0_5: Dp = 0.5.dp,
|
||||
val spacing2: Dp = 2.dp,
|
||||
val spacing4: Dp = 4.dp,
|
||||
val spacing6: Dp = 6.dp,
|
||||
val spacing8: Dp = 8.dp,
|
||||
val spacing10: Dp = 10.dp,
|
||||
val spacing12: Dp = 12.dp,
|
||||
|
|
@ -77,6 +78,7 @@ data class TangemDimens internal constructor(
|
|||
val spacing34: Dp = 34.dp,
|
||||
val spacing44: Dp = 44.dp,
|
||||
val spacing50: Dp = 50.dp,
|
||||
val spacing52: Dp = 52.dp,
|
||||
val spacing54: Dp = 54.dp,
|
||||
val spacing56: Dp = 56.dp,
|
||||
val spacing92: Dp = 92.dp,
|
||||
|
|
|
|||
10
core/ui/src/main/res/drawable/ic_search_24.xml
Normal file
10
core/ui/src/main/res/drawable/ic_search_24.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M9.5,3C11.224,3 12.877,3.685 14.096,4.904C15.315,6.123 16,7.776 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16C7.776,16 6.123,15.315 4.904,14.096C3.685,12.877 3,11.224 3,9.5C3,7.776 3.685,6.123 4.904,4.904C6.123,3.685 7.776,3 9.5,3ZM9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z" />
|
||||
</vector>
|
||||
|
|
@ -1,7 +1,21 @@
|
|||
package com.tangem.feature.swap.models
|
||||
|
||||
data class SwapSelectTokenStateHolder(
|
||||
val tokens: List<TokenToSelect>,
|
||||
val addedTokens: List<TokenToSelect>,
|
||||
val otherTokens: List<TokenToSelect>,
|
||||
val onSearchEntered: (String) -> Unit,
|
||||
val onTokenSelected: (String) -> Unit,
|
||||
)
|
||||
|
||||
data class TokenToSelect(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val iconUrl: String,
|
||||
val addedTokenBalanceData: TokenBalanceData? = null,
|
||||
)
|
||||
|
||||
data class TokenBalanceData(
|
||||
val amount: String?,
|
||||
val amountEquivalent: String?,
|
||||
)
|
||||
|
|
@ -22,18 +22,6 @@ data class SwapStateHolder(
|
|||
val onSuccess: (() -> Unit)? = null,
|
||||
)
|
||||
|
||||
data class TokenToSelect(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val addedTokenBalanceData: TokenBalanceData? = null,
|
||||
)
|
||||
|
||||
data class TokenBalanceData(
|
||||
val amount: String?,
|
||||
val amountEquivalent: String?,
|
||||
)
|
||||
|
||||
data class SwapCardData(
|
||||
val type: TransactionCardType,
|
||||
val amount: String?,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.fragment.app.viewModels
|
|||
import com.tangem.feature.swap.router.SwapRouter
|
||||
import com.tangem.feature.swap.router.SwapScreen
|
||||
import com.tangem.feature.swap.ui.SwapScreen
|
||||
import com.tangem.feature.swap.ui.SwapSelectTokenScreen
|
||||
import com.tangem.feature.swap.ui.SwapSuccessScreen
|
||||
import com.tangem.feature.swap.viewmodels.SwapViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
|
@ -38,7 +39,18 @@ class SwapFragment : Fragment() {
|
|||
SwapScreen(stateHolder = viewModel.uiState)
|
||||
}
|
||||
}
|
||||
SwapScreen.SelectToken -> SwapScreen(stateHolder = viewModel.uiState)// TODO: use proper screen
|
||||
SwapScreen.SelectToken -> {
|
||||
val tokenState = viewModel.uiState.selectTokenState
|
||||
if (tokenState != null) {
|
||||
SwapSelectTokenScreen(
|
||||
state = tokenState,
|
||||
onBack = viewModel.uiState.onBackClicked,
|
||||
)
|
||||
} else {
|
||||
SwapScreen(stateHolder = viewModel.uiState)
|
||||
}
|
||||
}
|
||||
// screen
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,237 @@
|
|||
@file:OptIn(ExperimentalFoundationApi::class)
|
||||
|
||||
package com.tangem.feature.swap.ui
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.components.CurrencyPlaceholderIcon
|
||||
import com.tangem.core.ui.components.SpacerW2
|
||||
import com.tangem.core.ui.components.appbar.ExpandableSearchView
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.swap.models.SwapSelectTokenStateHolder
|
||||
import com.tangem.feature.swap.models.TokenBalanceData
|
||||
import com.tangem.feature.swap.models.TokenToSelect
|
||||
import com.tangem.feature.swap.presentation.R
|
||||
import com.valentinilk.shimmer.shimmer
|
||||
|
||||
@Composable
|
||||
fun SwapSelectTokenScreen(state: SwapSelectTokenStateHolder, onBack: () -> Unit) {
|
||||
|
||||
TangemTheme {
|
||||
Scaffold(
|
||||
content = {
|
||||
ListOfTokens(state = state)
|
||||
},
|
||||
topBar = {
|
||||
ExpandableSearchView(
|
||||
title = stringResource(R.string.swapping_token_list_your_title),
|
||||
onBackClick = onBack,
|
||||
placeholderSearchText = stringResource(id = R.string.search_tokens_title),
|
||||
onSearchChanged = state.onSearchEntered,
|
||||
onSearchDisplayClosed = { state.onSearchEntered("") },
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ListOfTokens(state: SwapSelectTokenStateHolder) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(color = TangemTheme.colors.background.primary)
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing32,
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
|
||||
) {
|
||||
|
||||
stickyHeader {
|
||||
Header(title = R.string.swapping_token_list_your_tokens)
|
||||
}
|
||||
|
||||
|
||||
itemsIndexed(items = state.addedTokens) { index, item ->
|
||||
TokenItem(token = item, onTokenClick = { state.onTokenSelected(item.id) })
|
||||
|
||||
if (index != state.addedTokens.lastIndex) Divider(
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
startIndent = TangemTheme.dimens.spacing54,
|
||||
)
|
||||
}
|
||||
|
||||
stickyHeader {
|
||||
Header(title = R.string.swapping_token_list_other_tokens)
|
||||
}
|
||||
|
||||
itemsIndexed(items = state.otherTokens) { index, item ->
|
||||
TokenItem(token = item, onTokenClick = { state.onTokenSelected(item.id) })
|
||||
if (index != state.otherTokens.lastIndex) Divider(
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
startIndent = TangemTheme.dimens.spacing54,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Header(@StringRes title: Int) {
|
||||
Text(
|
||||
text = stringResource(id = title).uppercase(),
|
||||
style = TangemTheme.typography.overline,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(color = TangemTheme.colors.background.primary)
|
||||
.padding(vertical = TangemTheme.dimens.spacing6),
|
||||
textAlign = TextAlign.Start,
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
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) },
|
||||
),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(end = TangemTheme.dimens.spacing12)
|
||||
.align(Alignment.CenterVertically),
|
||||
) {
|
||||
val iconModifier = Modifier
|
||||
.size(TangemTheme.dimens.size40)
|
||||
SubcomposeAsyncImage(
|
||||
modifier = iconModifier,
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(token.iconUrl)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
contentDescription = token.id,
|
||||
loading = { TokenImageShimmer(modifier = iconModifier) },
|
||||
error = { CurrencyPlaceholderIcon(modifier = iconModifier, id = token.id) },
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterVertically),
|
||||
) {
|
||||
Text(
|
||||
text = token.name,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
SpacerW2()
|
||||
Text(
|
||||
text = token.symbol,
|
||||
style = TangemTheme.typography.caption,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
if (token.addedTokenBalanceData != null) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.End,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens.spacing8),
|
||||
) {
|
||||
Text(
|
||||
text = token.addedTokenBalanceData.amount ?: "",
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
SpacerW2()
|
||||
Text(
|
||||
text = token.addedTokenBalanceData.amountEquivalent ?: "",
|
||||
style = TangemTheme.typography.caption,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenImageShimmer(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(modifier = modifier.shimmer()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.background(
|
||||
color = TangemTheme.colors.button.secondary,
|
||||
shape = CircleShape,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val token = TokenToSelect(
|
||||
id = "",
|
||||
name = "USDC",
|
||||
symbol = "USDC",
|
||||
iconUrl = "",
|
||||
addedTokenBalanceData = TokenBalanceData(
|
||||
amount = "15 000 $",
|
||||
amountEquivalent = "15 000 " +
|
||||
"USDT",
|
||||
),
|
||||
)
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun TokenScreenPreview() {
|
||||
SwapSelectTokenScreen(
|
||||
state = SwapSelectTokenStateHolder(
|
||||
listOf(token, token, token), listOf(token, token, token), {}, {},
|
||||
),
|
||||
) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.swap.ui
|
|||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -15,10 +16,10 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
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.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
|
|
@ -46,9 +47,9 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
import com.tangem.feature.swap.models.TransactionCardType
|
||||
import com.valentinilk.shimmer.shimmer
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun TransactionCard(
|
||||
modifier: Modifier = Modifier,
|
||||
type: TransactionCardType,
|
||||
amount: String?,
|
||||
amountEquivalent: String?,
|
||||
|
|
@ -56,15 +57,12 @@ fun TransactionCard(
|
|||
tokenCurrency: String,
|
||||
@DrawableRes networkIconRes: Int? = null,
|
||||
onChangeTokenClick: (() -> Unit)? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Card(
|
||||
modifier = modifier,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius12),
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
onClick = onChangeTokenClick ?: {},
|
||||
enabled = onChangeTokenClick != null,
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Column(
|
||||
|
|
@ -96,6 +94,13 @@ fun TransactionCard(
|
|||
Box(modifier = Modifier.align(Alignment.CenterEnd)) {
|
||||
ChangeTokenSelector()
|
||||
}
|
||||
Box(
|
||||
Modifier
|
||||
.align(Alignment.CenterEnd)
|
||||
.height(TangemTheme.dimens.size116)
|
||||
.width(TangemTheme.dimens.size102)
|
||||
.clickable { onChangeTokenClick() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,9 +14,12 @@ import com.tangem.feature.swap.models.FeeState
|
|||
import com.tangem.feature.swap.models.SwapButton
|
||||
import com.tangem.feature.swap.models.SwapCardData
|
||||
import com.tangem.feature.swap.models.SwapPermissionStateHolder
|
||||
import com.tangem.feature.swap.models.SwapSelectTokenStateHolder
|
||||
import com.tangem.feature.swap.models.SwapStateHolder
|
||||
import com.tangem.feature.swap.models.SwapSuccessStateHolder
|
||||
import com.tangem.feature.swap.models.SwapWarning
|
||||
import com.tangem.feature.swap.models.TokenBalanceData
|
||||
import com.tangem.feature.swap.models.TokenToSelect
|
||||
import com.tangem.feature.swap.models.TransactionCardType
|
||||
import com.tangem.feature.swap.presentation.R
|
||||
import com.tangem.feature.swap.router.SwapRouter
|
||||
|
|
@ -45,7 +48,7 @@ internal class SwapViewModel @Inject constructor(
|
|||
swapRouter = router
|
||||
uiState = uiState.copy(
|
||||
onBackClicked = router::back,
|
||||
onSelectTokenClick = { router.openScreen(SwapScreen.Success) },
|
||||
onSelectTokenClick = { router.openScreen(SwapScreen.SelectToken) },
|
||||
onSuccess = { router.openScreen(SwapScreen.Success) },
|
||||
)
|
||||
}
|
||||
|
|
@ -97,6 +100,19 @@ internal class SwapViewModel @Inject constructor(
|
|||
cancelButton = CancelPermissionButton(true) {},
|
||||
)
|
||||
|
||||
val token = TokenToSelect(
|
||||
id = "USDC",
|
||||
name = "USDC",
|
||||
symbol = "USDC",
|
||||
iconUrl = "",
|
||||
addedTokenBalanceData = TokenBalanceData(amount = "15 000 $", amountEquivalent = "15 000 USDT"),
|
||||
)
|
||||
|
||||
val selectTokenState = SwapSelectTokenStateHolder(
|
||||
listOf(token, token, token, token, token, token, token, token, token),
|
||||
listOf(token, token, token, token, token, token, token, token, token), {}, {},
|
||||
)
|
||||
|
||||
return SwapStateHolder(
|
||||
sendCardData = sendCard,
|
||||
receiveCardData = receiveCard,
|
||||
|
|
@ -107,6 +123,7 @@ internal class SwapViewModel @Inject constructor(
|
|||
onRefresh = {}, onBackClicked = {}, onChangeCardsClicked = {},
|
||||
permissionState = permissionState,
|
||||
successState = SwapSuccessStateHolder("Success"),
|
||||
selectTokenState = selectTokenState,
|
||||
)
|
||||
}
|
||||
// endregion temp for test
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue