Updated on 2026-08-14
This commit is contained in:
parent
8d6992ad24
commit
646fe2d349
13 changed files with 72 additions and 32 deletions
|
|
@ -17,7 +17,9 @@ import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
|||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.LinkAnnotation
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.withLink
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
|
|
@ -26,8 +28,8 @@ import com.tangem.common.ui.R
|
|||
import com.tangem.common.ui.bottomsheet.permission.state.*
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.containers.FooterContainer
|
||||
import com.tangem.core.ui.components.inputrow.InputRowDefault
|
||||
import com.tangem.core.ui.extensions.*
|
||||
|
|
@ -114,7 +116,22 @@ private fun GiveTxPermissionBottomSheetContent(content: GiveTxPermissionBottomSh
|
|||
@Composable
|
||||
private fun ApprovalBottomSheetInfo(data: GiveTxPermissionState.ReadyForRequest) {
|
||||
FooterContainer(
|
||||
footer = resourceReference(R.string.give_permission_policy_type_footer),
|
||||
footer = annotatedReference {
|
||||
append(stringResourceSafe(R.string.swap_approve_description))
|
||||
append(" ")
|
||||
withLink(
|
||||
link = LinkAnnotation.Clickable(
|
||||
tag = "APPROVE_TAG",
|
||||
linkInteractionListener = { data.onOpenLearnMoreAboutApproveClick() },
|
||||
),
|
||||
block = {
|
||||
appendColored(
|
||||
text = stringResourceSafe(R.string.common_learn_more),
|
||||
color = TangemTheme.colors.text.accent,
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
AmountItem(
|
||||
|
|
@ -318,6 +335,7 @@ private val previewData = GiveTxPermissionBottomSheetConfig(
|
|||
subtitle = resourceReference(R.string.give_permission_staking_subtitle, wrappedList("1")),
|
||||
dialogText = resourceReference(R.string.give_permission_staking_footer),
|
||||
footerText = resourceReference(R.string.swap_give_permission_fee_footer),
|
||||
onOpenLearnMoreAboutApproveClick = {},
|
||||
),
|
||||
walletInteractionIcon = R.drawable.ic_tangem_24,
|
||||
onCancel = {},
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ sealed class GiveTxPermissionState {
|
|||
val approveButton: ApprovePermissionButton,
|
||||
val cancelButton: CancelPermissionButton,
|
||||
val onChangeApproveType: ((ApproveType) -> Unit)? = null,
|
||||
val onOpenLearnMoreAboutApproveClick: () -> Unit,
|
||||
) : GiveTxPermissionState()
|
||||
|
||||
fun GiveTxPermissionState.getApproveTypeOrNull(): ApproveType? {
|
||||
|
|
|
|||
|
|
@ -18,4 +18,6 @@ object TangemLinks {
|
|||
append("/blog/post/what-is-a-transaction-fee-and-why-do-we-need-it/")
|
||||
}
|
||||
}
|
||||
|
||||
const val RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP = "https://tangem.com/en/blog/post/give-revoke-permission/"
|
||||
}
|
||||
|
|
@ -67,4 +67,6 @@ internal interface StakingClickIntents : AmountScreenClickIntents {
|
|||
fun openTokenDetails(cryptoCurrency: CryptoCurrency)
|
||||
|
||||
fun showPrimaryClickAlert()
|
||||
|
||||
fun onOpenLearnMoreAboutApproveClick()
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import com.tangem.core.decompose.di.ModelScoped
|
|||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.haptic.TangemHapticEffect
|
||||
|
|
@ -73,6 +74,7 @@ import com.tangem.features.staking.impl.presentation.state.utils.checkAndCalcula
|
|||
import com.tangem.features.staking.impl.presentation.state.utils.isSingleAction
|
||||
import com.tangem.features.staking.impl.presentation.state.utils.withStubUnstakeAction
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.TangemLinks.RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP
|
||||
import com.tangem.utils.coroutines.*
|
||||
import com.tangem.utils.extensions.isSingleItem
|
||||
import com.tangem.utils.extensions.orZero
|
||||
|
|
@ -123,6 +125,7 @@ internal class StakingModel @Inject constructor(
|
|||
private val getActionRequirementAmountUseCase: GetActionRequirementAmountUseCase,
|
||||
private val paramsInterceptorHolder: ParamsInterceptorHolder,
|
||||
private val shareManager: ShareManager,
|
||||
private val urlOpener: UrlOpener,
|
||||
@DelayedWork private val coroutineScope: CoroutineScope,
|
||||
private val innerRouter: InnerStakingRouter,
|
||||
appRouter: AppRouter,
|
||||
|
|
@ -927,6 +930,10 @@ internal class StakingModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
override fun onOpenLearnMoreAboutApproveClick() {
|
||||
urlOpener.openUrl(RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP)
|
||||
}
|
||||
|
||||
private suspend fun setupApprovalNeeded() {
|
||||
val approval = StakingIntegrationID.create(currencyId = cryptoCurrencyStatus.currency.id)?.approval
|
||||
?: StakingApproval.Empty
|
||||
|
|
|
|||
|
|
@ -76,4 +76,6 @@ internal object StakingClickIntentsStub : StakingClickIntents {
|
|||
override fun onActiveStakeAnalytic() {}
|
||||
|
||||
override fun showPrimaryClickAlert() {}
|
||||
|
||||
override fun onOpenLearnMoreAboutApproveClick() {}
|
||||
}
|
||||
|
|
@ -77,6 +77,7 @@ internal class ShowApprovalBottomSheetTransformer(
|
|||
dialogText = resourceReference(R.string.give_permission_staking_footer),
|
||||
footerText = resourceReference(R.string.staking_give_permission_fee_footer),
|
||||
onChangeApproveType = prevState.clickIntents::onApproveTypeChange,
|
||||
onOpenLearnMoreAboutApproveClick = {},
|
||||
),
|
||||
walletInteractionIcon = walletInterationIcon(userWallet),
|
||||
onCancel = onDismiss,
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ import com.tangem.feature.swap.ui.StateBuilder
|
|||
import com.tangem.feature.swap.utils.formatToUIRepresentation
|
||||
import com.tangem.features.swap.SwapComponent
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.TangemLinks.RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP
|
||||
import com.tangem.utils.coroutines.*
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
|
|
@ -1164,6 +1165,9 @@ internal class SwapModel @Inject constructor(
|
|||
modelScope.launch { shouldShowStoriesUseCase.neverToShow(StoryContentIds.STORY_FIRST_TIME_SWAP.id) }
|
||||
swapRouter.openScreen(SwapNavScreen.Main)
|
||||
},
|
||||
onOpenLearnMoreAboutApproveClick = {
|
||||
urlOpener.openUrl(RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -243,6 +243,7 @@ internal class SwapNotificationsFactory(
|
|||
SwapNotificationUM.Info.PermissionNeeded(
|
||||
providerName = fromToken.symbol,
|
||||
fromTokenSymbol = providerName,
|
||||
onApproveClick = actions.openPermissionBottomSheet,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,4 +32,5 @@ data class UiActions(
|
|||
val onSuccess: () -> Unit,
|
||||
val onLinkClick: (String) -> Unit,
|
||||
val onReceiveCardWarningClick: () -> Unit,
|
||||
val onOpenLearnMoreAboutApproveClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -199,6 +199,7 @@ internal object SwapNotificationUM {
|
|||
data class PermissionNeeded(
|
||||
val providerName: String,
|
||||
val fromTokenSymbol: String,
|
||||
val onApproveClick: () -> Unit,
|
||||
) : Info(
|
||||
title = resourceReference(R.string.express_provider_permission_needed),
|
||||
subtitle = resourceReference(
|
||||
|
|
@ -206,6 +207,10 @@ internal object SwapNotificationUM {
|
|||
formatArgs = wrappedList(providerName, fromTokenSymbol),
|
||||
),
|
||||
iconResId = R.drawable.ic_locked_24,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = TextReference.Res(R.string.common_approve),
|
||||
onClick = onApproveClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -347,6 +347,7 @@ internal class StateBuilder(
|
|||
providerName = swapProvider.name,
|
||||
onGivePermissionClick = actions.onGivePermissionClick,
|
||||
onChangeApproveType = actions.onChangeApproveType,
|
||||
onOpenLearnMoreAboutApproveClick = actions.onOpenLearnMoreAboutApproveClick,
|
||||
),
|
||||
fee = feeState,
|
||||
swapButton = SwapButton(
|
||||
|
|
@ -411,7 +412,8 @@ internal class StateBuilder(
|
|||
it is SwapNotificationUM.Error || it is NotificationUM.Error ||
|
||||
it is SwapNotificationUM.Warning.ExpressError || it is SwapNotificationUM.Warning.ExpressGeneralError ||
|
||||
it is SwapNotificationUM.Warning.NoAvailableTokensToSwap ||
|
||||
it is SwapNotificationUM.Warning.NeedReserveToCreateAccount
|
||||
it is SwapNotificationUM.Warning.NeedReserveToCreateAccount ||
|
||||
it is SwapNotificationUM.Info.PermissionNeeded
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -934,12 +936,14 @@ internal class StateBuilder(
|
|||
)
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
private fun convertPermissionState(
|
||||
lastPermissionState: GiveTxPermissionState,
|
||||
permissionDataState: PermissionDataState,
|
||||
providerName: String,
|
||||
onGivePermissionClick: () -> Unit,
|
||||
onChangeApproveType: (ApproveType) -> Unit,
|
||||
onOpenLearnMoreAboutApproveClick: () -> Unit,
|
||||
): GiveTxPermissionState {
|
||||
val approveType = if (lastPermissionState is GiveTxPermissionState.ReadyForRequest) {
|
||||
lastPermissionState.approveType
|
||||
|
|
@ -977,6 +981,7 @@ internal class StateBuilder(
|
|||
),
|
||||
dialogText = resourceReference(R.string.swapping_approve_information_text),
|
||||
footerText = resourceReference(R.string.swap_give_permission_fee_footer),
|
||||
onOpenLearnMoreAboutApproveClick = onOpenLearnMoreAboutApproveClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ internal fun SwapScreenContent(state: SwapStateHolder, modifier: Modifier = Modi
|
|||
|
||||
if (state.notifications.isNotEmpty()) SwapNotifications(notifications = state.notifications)
|
||||
|
||||
MainButton(state = state, onPermissionWarningClick = state.onShowPermissionBottomSheet)
|
||||
SpacerHMax()
|
||||
|
||||
if (state.tosState != null && state.providerState !is ProviderState.Empty) {
|
||||
ProviderTos(
|
||||
|
|
@ -87,6 +87,8 @@ internal fun SwapScreenContent(state: SwapStateHolder, modifier: Modifier = Modi
|
|||
.padding(top = TangemTheme.dimens.spacing16),
|
||||
)
|
||||
}
|
||||
|
||||
MainButton(state = state)
|
||||
}
|
||||
|
||||
if (state.shouldShowMaxAmount && keyboard is Keyboard.Opened) {
|
||||
|
|
@ -359,34 +361,22 @@ private fun SwapNotifications(notifications: List<NotificationUM>) {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun MainButton(state: SwapStateHolder, onPermissionWarningClick: () -> Unit) {
|
||||
// order is important
|
||||
when {
|
||||
state.isInsufficientFunds -> {
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResourceSafe(id = R.string.swapping_insufficient_funds),
|
||||
enabled = false,
|
||||
onClick = state.swapButton.onClick,
|
||||
)
|
||||
}
|
||||
state.notifications.any { it is SwapNotificationUM.Info.PermissionNeeded } -> {
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResourceSafe(id = R.string.give_permission_title),
|
||||
enabled = true,
|
||||
onClick = onPermissionWarningClick,
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
PrimaryButtonIconEnd(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResourceSafe(id = R.string.swapping_swap_action),
|
||||
iconResId = state.swapButton.walletInteractionIcon,
|
||||
enabled = state.swapButton.enabled,
|
||||
onClick = state.swapButton.onClick,
|
||||
)
|
||||
}
|
||||
private fun MainButton(state: SwapStateHolder) {
|
||||
if (state.isInsufficientFunds) {
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResourceSafe(id = R.string.swapping_insufficient_funds),
|
||||
enabled = false,
|
||||
onClick = state.swapButton.onClick,
|
||||
)
|
||||
} else {
|
||||
PrimaryButtonIconEnd(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResourceSafe(id = R.string.swapping_swap_action),
|
||||
iconResId = state.swapButton.walletInteractionIcon,
|
||||
enabled = state.swapButton.enabled,
|
||||
onClick = state.swapButton.onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -438,6 +428,7 @@ private val state = SwapStateHolder(
|
|||
SwapNotificationUM.Info.PermissionNeeded(
|
||||
providerName = "Provider",
|
||||
fromTokenSymbol = "POL",
|
||||
onApproveClick = {},
|
||||
),
|
||||
SwapNotificationUM.Warning.NoAvailableTokensToSwap("POLYGON"),
|
||||
),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue