Updated on 2026-08-14
This commit is contained in:
commit
8ef0c9f5a6
5 changed files with 118 additions and 124 deletions
|
|
@ -53,6 +53,7 @@ dependencies {
|
|||
implementation(project(":core:ui"))
|
||||
|
||||
/** AndroidX */
|
||||
implementation(AndroidX.activityCompose)
|
||||
implementation(AndroidX.appCompat)
|
||||
implementation(AndroidX.fragmentKtx)
|
||||
implementation(AndroidX.lifecycleViewModelKtx)
|
||||
|
|
|
|||
|
|
@ -187,9 +187,7 @@ class StateBuilder(val actions: UiActions) {
|
|||
}
|
||||
|
||||
fun addTokensToState(uiState: SwapStateHolder, dataState: FoundTokensState): SwapStateHolder {
|
||||
return uiState.copy(
|
||||
selectTokenState = tokensDataConverter.convert(dataState),
|
||||
)
|
||||
return uiState.copy(selectTokenState = tokensDataConverter.convert(dataState))
|
||||
}
|
||||
|
||||
private fun convertPermissionState(
|
||||
|
|
@ -203,8 +201,8 @@ class StateBuilder(val actions: UiActions) {
|
|||
is PermissionDataState.PermissionReadyForRequest -> SwapPermissionState.ReadyForRequest(
|
||||
currency = permissionDataState.currency,
|
||||
amount = permissionDataState.amount,
|
||||
walletAddress = permissionDataState.walletAddress,
|
||||
spenderAddress = permissionDataState.spenderAddress,
|
||||
walletAddress = getShortAddressValue(permissionDataState.walletAddress),
|
||||
spenderAddress = getShortAddressValue(permissionDataState.spenderAddress),
|
||||
fee = permissionDataState.fee,
|
||||
approveButton = ApprovePermissionButton(
|
||||
enabled = true,
|
||||
|
|
@ -287,9 +285,21 @@ class StateBuilder(val actions: UiActions) {
|
|||
)
|
||||
}
|
||||
|
||||
fun clearAlert(uiState: SwapStateHolder): SwapStateHolder {
|
||||
return uiState.copy(
|
||||
alert = null,
|
||||
fun clearAlert(uiState: SwapStateHolder): SwapStateHolder = uiState.copy(alert = null)
|
||||
|
||||
private fun getShortAddressValue(fullAddress: String): String {
|
||||
check(fullAddress.length > ADDRESS_MIN_LENGTH) { "Invalid address" }
|
||||
val firstAddressPart = fullAddress.substring(startIndex = 0, endIndex = ADDRESS_FIRST_PART_LENGTH)
|
||||
val secondAddressPart = fullAddress.substring(
|
||||
startIndex = fullAddress.length - ADDRESS_SECOND_PART_LENGTH,
|
||||
endIndex = fullAddress.length,
|
||||
)
|
||||
return "$firstAddressPart...$secondAddressPart"
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val ADDRESS_MIN_LENGTH = 11
|
||||
const val ADDRESS_FIRST_PART_LENGTH = 7
|
||||
const val ADDRESS_SECOND_PART_LENGTH = 4
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.swap.ui
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -22,7 +23,6 @@ import com.tangem.core.ui.components.SpacerH10
|
|||
import com.tangem.core.ui.components.SpacerH12
|
||||
import com.tangem.core.ui.components.SpacerH16
|
||||
import com.tangem.core.ui.components.SpacerH28
|
||||
import com.tangem.core.ui.components.SpacerH32
|
||||
import com.tangem.core.ui.components.atoms.Hand
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.swap.models.ApprovePermissionButton
|
||||
|
|
@ -31,10 +31,9 @@ import com.tangem.feature.swap.models.SwapPermissionState
|
|||
import com.tangem.feature.swap.presentation.R
|
||||
|
||||
@Composable
|
||||
fun SwapPermissionBottomSheetContent(
|
||||
data: SwapPermissionState.ReadyForRequest?,
|
||||
onCancel: () -> Unit,
|
||||
) {
|
||||
fun SwapPermissionBottomSheetContent(data: SwapPermissionState.ReadyForRequest, onCancel: () -> Unit) {
|
||||
BackHandler(onBack = onCancel)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(color = TangemTheme.colors.background.primary)
|
||||
|
|
@ -57,7 +56,7 @@ fun SwapPermissionBottomSheetContent(
|
|||
Text(
|
||||
text = stringResource(
|
||||
id = R.string.swapping_permission_subheader,
|
||||
data?.currency ?: "",
|
||||
data.currency,
|
||||
),
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
style = TangemTheme.typography.body2,
|
||||
|
|
@ -67,13 +66,7 @@ fun SwapPermissionBottomSheetContent(
|
|||
|
||||
SpacerH16()
|
||||
|
||||
ApprovalBottomSheetInfo(
|
||||
currency = data?.currency ?: "",
|
||||
amount = data?.amount ?: "",
|
||||
walletAddress = data?.walletAddress ?: "",
|
||||
spenderAddress = data?.spenderAddress ?: "",
|
||||
fee = data?.fee ?: "",
|
||||
)
|
||||
ApprovalBottomSheetInfo(data)
|
||||
|
||||
SpacerH28()
|
||||
|
||||
|
|
@ -81,7 +74,7 @@ fun SwapPermissionBottomSheetContent(
|
|||
text = stringResource(id = R.string.swapping_permission_buttons_approve),
|
||||
icon = painterResource(id = R.drawable.ic_tangem_24),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = { data?.approveButton?.onClick?.invoke() },
|
||||
onClick = data.approveButton.onClick,
|
||||
)
|
||||
|
||||
SpacerH12()
|
||||
|
|
@ -90,23 +83,17 @@ fun SwapPermissionBottomSheetContent(
|
|||
text = stringResource(id = R.string.common_cancel),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = {
|
||||
data?.cancelButton?.onClick?.invoke()
|
||||
data.cancelButton.onClick()
|
||||
onCancel()
|
||||
},
|
||||
)
|
||||
|
||||
SpacerH32()
|
||||
SpacerH16()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ApprovalBottomSheetInfo(
|
||||
currency: String,
|
||||
amount: String,
|
||||
walletAddress: String,
|
||||
spenderAddress: String,
|
||||
fee: String,
|
||||
) {
|
||||
private fun ApprovalBottomSheetInfo(data: SwapPermissionState.ReadyForRequest) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(color = TangemTheme.colors.background.primary)
|
||||
|
|
@ -117,13 +104,13 @@ private fun ApprovalBottomSheetInfo(
|
|||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
AmountItem(currency = currency, amount = amount)
|
||||
AmountItem(currency = data.currency, amount = data.amount)
|
||||
DividerBottomSheet()
|
||||
WalletAddressItem(walletAddress = walletAddress)
|
||||
WalletAddressItem(walletAddress = data.walletAddress)
|
||||
DividerBottomSheet()
|
||||
SpenderItem(spenderAddress = spenderAddress)
|
||||
SpenderItem(spenderAddress = data.spenderAddress)
|
||||
DividerBottomSheet()
|
||||
FeeItem(fee = fee)
|
||||
FeeItem(fee = data.fee)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -138,11 +125,11 @@ private fun DividerBottomSheet() {
|
|||
@Composable
|
||||
private fun InformationItem(subtitle: String, value: String) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(TangemTheme.dimens.spacing16),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = subtitle,
|
||||
|
|
@ -151,13 +138,11 @@ private fun InformationItem(subtitle: String, value: String) {
|
|||
maxLines = 1,
|
||||
)
|
||||
|
||||
SpacerH16()
|
||||
|
||||
MiddleEllipsisText(
|
||||
text = value,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.body2,
|
||||
modifier = Modifier.padding(start = TangemTheme.dimens.spacing16),
|
||||
modifier = Modifier.padding(start = TangemTheme.dimens.spacing16)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,66 +1,56 @@
|
|||
package com.tangem.feature.swap.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.BottomSheetScaffold
|
||||
import androidx.compose.material.BottomSheetState
|
||||
import androidx.compose.material.BottomSheetValue
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.rememberBottomSheetScaffoldState
|
||||
import androidx.compose.material.ModalBottomSheetLayout
|
||||
import androidx.compose.material.ModalBottomSheetValue
|
||||
import androidx.compose.material.rememberModalBottomSheetState
|
||||
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
|
||||
import com.tangem.feature.swap.models.SwapPermissionState
|
||||
import com.tangem.feature.swap.models.SwapStateHolder
|
||||
import com.tangem.feature.swap.presentation.R
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
internal fun SwapScreen(stateHolder: SwapStateHolder) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(
|
||||
bottomSheetState = BottomSheetState(BottomSheetValue.Collapsed),
|
||||
val bottomSheetState = rememberModalBottomSheetState(
|
||||
initialValue = ModalBottomSheetValue.Hidden,
|
||||
skipHalfExpanded = true,
|
||||
)
|
||||
|
||||
TangemTheme {
|
||||
BottomSheetScaffold(
|
||||
modifier = Modifier.background(TangemTheme.colors.background.secondary),
|
||||
topBar = {
|
||||
AppBarWithBackButton(
|
||||
text = stringResource(R.string.swapping_swap),
|
||||
onBackClick = stateHolder.onBackClicked,
|
||||
iconRes = R.drawable.ic_close_24,
|
||||
)
|
||||
},
|
||||
ModalBottomSheetLayout(
|
||||
sheetContent = {
|
||||
SwapPermissionBottomSheetContent(
|
||||
data = stateHolder.permissionState as? SwapPermissionState.ReadyForRequest,
|
||||
onCancel = { coroutineScope.launch { bottomSheetScaffoldState.bottomSheetState.collapse() } },
|
||||
)
|
||||
if (stateHolder.permissionState is SwapPermissionState.ReadyForRequest) {
|
||||
SwapPermissionBottomSheetContent(
|
||||
data = stateHolder.permissionState,
|
||||
onCancel = { coroutineScope.launch { bottomSheetState.hide() } },
|
||||
)
|
||||
} else {
|
||||
// Required "else" block to prevent compose crash
|
||||
Box(modifier = Modifier.fillMaxSize())
|
||||
}
|
||||
},
|
||||
scaffoldState = bottomSheetScaffoldState,
|
||||
sheetState = bottomSheetState,
|
||||
sheetShape = RoundedCornerShape(
|
||||
topStart = TangemTheme.dimens.radius16,
|
||||
topEnd = TangemTheme.dimens.radius16,
|
||||
),
|
||||
sheetElevation = TangemTheme.dimens.elevation24,
|
||||
sheetPeekHeight = TangemTheme.dimens.size0,
|
||||
content = {
|
||||
SwapScreenContent(
|
||||
state = stateHolder,
|
||||
onPermissionWarningClick = {
|
||||
val isBottomSheetReady = !bottomSheetState.isVisible &&
|
||||
stateHolder.permissionState is SwapPermissionState.ReadyForRequest
|
||||
coroutineScope.launch {
|
||||
if (bottomSheetScaffoldState.bottomSheetState.isCollapsed &&
|
||||
stateHolder.permissionState is SwapPermissionState.ReadyForRequest
|
||||
) {
|
||||
bottomSheetScaffoldState.bottomSheetState.expand()
|
||||
} else {
|
||||
bottomSheetScaffoldState.bottomSheetState.collapse()
|
||||
}
|
||||
if (isBottomSheetReady) bottomSheetState.show() else bottomSheetState.hide()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import com.tangem.core.ui.components.SimpleOkDialog
|
|||
import com.tangem.core.ui.components.SmallInfoCard
|
||||
import com.tangem.core.ui.components.SmallInfoCardWithWarning
|
||||
import com.tangem.core.ui.components.SpacerH16
|
||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
||||
import com.tangem.core.ui.components.keyboardAsState
|
||||
import com.tangem.core.ui.extensions.getActiveIconRes
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -59,62 +60,69 @@ internal fun SwapScreenContent(state: SwapStateHolder, onPermissionWarningClick:
|
|||
.fillMaxSize()
|
||||
.background(color = TangemTheme.colors.background.secondary),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
top = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing32,
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
MainInfo(state)
|
||||
Column {
|
||||
AppBarWithBackButton(
|
||||
text = stringResource(R.string.swapping_swap),
|
||||
onBackClick = state.onBackClicked,
|
||||
iconRes = R.drawable.ic_close_24,
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
top = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing32,
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
MainInfo(state)
|
||||
|
||||
FeeItem(feeState = state.fee, currency = state.networkCurrency)
|
||||
FeeItem(feeState = state.fee, currency = state.networkCurrency)
|
||||
|
||||
if (state.warnings.isNotEmpty()) {
|
||||
SwapWarnings(
|
||||
warnings = state.warnings,
|
||||
onPermissionWarningClick = onPermissionWarningClick,
|
||||
)
|
||||
}
|
||||
if (state.warnings.isNotEmpty()) {
|
||||
SwapWarnings(
|
||||
warnings = state.warnings,
|
||||
onPermissionWarningClick = onPermissionWarningClick,
|
||||
)
|
||||
}
|
||||
|
||||
if (state.permissionState is SwapPermissionState.InProgress) {
|
||||
CardWithIcon(
|
||||
title = stringResource(id = R.string.swapping_pending_transaction_title),
|
||||
description = stringResource(id = R.string.swapping_pending_transaction_subtitle),
|
||||
icon = {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size16),
|
||||
color = TangemTheme.colors.icon.primary1,
|
||||
strokeWidth = TangemTheme.dimens.size2,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
if (state.permissionState is SwapPermissionState.InProgress) {
|
||||
CardWithIcon(
|
||||
title = stringResource(id = R.string.swapping_pending_transaction_title),
|
||||
description = stringResource(id = R.string.swapping_pending_transaction_subtitle),
|
||||
icon = {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size16),
|
||||
color = TangemTheme.colors.icon.primary1,
|
||||
strokeWidth = TangemTheme.dimens.size2,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
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,
|
||||
)
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue