From 98a62640d1905acbb8d9e289cb6a253201616406 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 24 Jan 2023 21:21:24 +0800 Subject: [PATCH] Updated on 2026-08-14 --- features/swap/presentation/build.gradle.kts | 1 + .../tangem/feature/swap/ui/StateBuilder.kt | 26 ++-- .../ui/SwapPermissionBottomSheetContent.kt | 49 +++----- .../com/tangem/feature/swap/ui/SwapScreen.kt | 54 ++++----- .../feature/swap/ui/SwapScreenContent.kt | 112 ++++++++++-------- 5 files changed, 118 insertions(+), 124 deletions(-) diff --git a/features/swap/presentation/build.gradle.kts b/features/swap/presentation/build.gradle.kts index ab809d50b1..969e973160 100644 --- a/features/swap/presentation/build.gradle.kts +++ b/features/swap/presentation/build.gradle.kts @@ -53,6 +53,7 @@ dependencies { implementation(project(":core:ui")) /** AndroidX */ + implementation(AndroidX.activityCompose) implementation(AndroidX.appCompat) implementation(AndroidX.fragmentKtx) implementation(AndroidX.lifecycleViewModelKtx) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index f035626a4b..a4a4f38520 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -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 } } \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheetContent.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheetContent.kt index 727ae7d2ec..e67b98b3da 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheetContent.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheetContent.kt @@ -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) ) } } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreen.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreen.kt index bbc2c92a36..055d2f7db8 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreen.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreen.kt @@ -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() } }, ) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt index e4457e6552..4a8627a760 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt @@ -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, + ) + } } }