Updated on 2026-08-14

This commit is contained in:
Tangem 2025-06-27 15:27:09 +05:00
parent 80595f8381
commit 4218aea2bd
14 changed files with 148 additions and 56 deletions

View file

@ -0,0 +1,47 @@
package com.tangem.common.ui.notifications
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.res.TangemTheme
import kotlinx.collections.immutable.ImmutableList
fun LazyListScope.notifications(
notifications: ImmutableList<NotificationUM>,
modifier: Modifier = Modifier,
hasPaddingAbove: Boolean = false,
isClickDisabled: Boolean = false,
) {
itemsIndexed(
items = notifications,
key = { _, item -> item::class.java },
contentType = { _, item -> item::class.java },
itemContent = { i, item ->
val topPadding = if (i == 0 && hasPaddingAbove) 0.dp else 12.dp
Notification(
config = item.config,
modifier = modifier
.padding(top = topPadding)
.animateItem(),
containerColor = when (item) {
is NotificationUM.Error.TokenExceedsBalance,
is NotificationUM.Warning.NetworkFeeUnreachable,
is NotificationUM.Warning.HighFeeError,
-> TangemTheme.colors.background.action
else -> TangemTheme.colors.button.disabled
},
iconTint = when (item) {
is NotificationUM.Error.TokenExceedsBalance,
is NotificationUM.Warning,
-> null
is NotificationUM.Error -> TangemTheme.colors.icon.warning
is NotificationUM.Info -> TangemTheme.colors.icon.accent
},
isEnabled = !isClickDisabled,
)
},
)
}

View file

@ -13,6 +13,9 @@ dependencies {
implementation(projects.core.decompose)
implementation(projects.core.ui)
/** Common */
implementation(projects.common.ui)
/** Domain models */
api(projects.domain.models)
implementation(projects.domain.appCurrency.models)
@ -23,10 +26,12 @@ dependencies {
/** Compose */
implementation(deps.compose.runtime)
implementation(deps.compose.foundation)
/** Tangem */
implementation(tangemDeps.blockchain)
/** Other */
implementation(deps.arrow.core)
implementation(deps.kotlin.immutable.collections)
}

View file

@ -1,41 +1,28 @@
package com.tangem.features.send.v2.subcomponents.notifications
package com.tangem.features.send.v2.api
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.ui.Modifier
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.send.v2.subcomponents.notifications
import com.tangem.features.send.v2.subcomponents.notifications.model.NotificationData
import com.tangem.features.send.v2.subcomponents.notifications.model.NotificationsModel
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.flow.StateFlow
import java.math.BigDecimal
internal class NotificationsComponent(
appComponentContext: AppComponentContext,
params: Params,
) : AppComponentContext by appComponentContext {
interface SendNotificationsComponent {
private val model: NotificationsModel = getOrCreateModel(params)
val state: StateFlow<ImmutableList<NotificationUM>> = model.uiState
val state: StateFlow<ImmutableList<NotificationUM>>
fun LazyListScope.content(
state: ImmutableList<NotificationUM>,
modifier: Modifier = Modifier,
hasPaddingAbove: Boolean = false,
isClickDisabled: Boolean = false,
) {
notifications(
notifications = state,
modifier = modifier,
hasPaddingAbove = hasPaddingAbove,
isClickDisabled = isClickDisabled,
)
}
)
data class Params(
val analyticsCategoryName: String,
@ -44,5 +31,17 @@ internal class NotificationsComponent(
val feeCryptoCurrencyStatus: CryptoCurrencyStatus,
val appCurrency: AppCurrency,
val notificationData: NotificationData,
)
) {
data class NotificationData(
val destinationAddress: String,
val memo: String?,
val amountValue: BigDecimal,
val reduceAmountBy: BigDecimal,
val isIgnoreReduce: Boolean,
val fee: Fee?,
val feeError: GetFeeError?,
)
}
interface Factory : ComponentFactory<Params, SendNotificationsComponent>
}

View file

@ -5,8 +5,10 @@ import com.tangem.features.send.v2.DefaultSendFeatureToggles
import com.tangem.features.send.v2.api.NFTSendComponent
import com.tangem.features.send.v2.api.SendComponent
import com.tangem.features.send.v2.api.SendFeatureToggles
import com.tangem.features.send.v2.api.SendNotificationsComponent
import com.tangem.features.send.v2.send.DefaultSendComponent
import com.tangem.features.send.v2.sendnft.DefaultNFTSendComponent
import com.tangem.features.send.v2.subcomponents.notifications.DefaultSendNotificationsComponent
import dagger.Binds
import dagger.Module
import dagger.Provides
@ -35,4 +37,10 @@ internal interface SendFeatureModuleBinds {
@Binds
@Singleton
fun provideNFTSendComponentFactory(impl: DefaultNFTSendComponent.Factory): NFTSendComponent.Factory
@Binds
@Singleton
fun provideNotificationComponentFactory(
impl: DefaultSendNotificationsComponent.Factory,
): SendNotificationsComponent.Factory
}

View file

@ -14,6 +14,8 @@ import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.send.v2.api.SendNotificationsComponent
import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.PredefinedValues
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
@ -26,8 +28,7 @@ import com.tangem.features.send.v2.subcomponents.destination.SendDestinationBloc
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationComponentParams.DestinationBlockParams
import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponentParams
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsComponent
import com.tangem.features.send.v2.subcomponents.notifications.model.NotificationData
import com.tangem.features.send.v2.subcomponents.notifications.DefaultSendNotificationsComponent
import com.tangem.utils.extensions.orZero
import kotlinx.coroutines.flow.*
@ -89,9 +90,9 @@ internal class SendConfirmComponent(
onClick = model::showEditFee,
)
private val notificationsComponent = NotificationsComponent(
private val notificationsComponent = DefaultSendNotificationsComponent(
appComponentContext = child("sendConfirmNotifications"),
params = NotificationsComponent.Params(
params = SendNotificationsComponent.Params(
analyticsCategoryName = params.analyticsCategoryName,
userWalletId = params.userWallet.walletId,
cryptoCurrencyStatus = params.cryptoCurrencyStatus,

View file

@ -32,6 +32,7 @@ import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.SendBalanceUpdater
import com.tangem.features.send.v2.common.SendConfirmAlertFactory
@ -55,7 +56,6 @@ import com.tangem.features.send.v2.subcomponents.fee.model.checkAndCalculateSubt
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsUpdateTrigger
import com.tangem.features.send.v2.subcomponents.notifications.model.NotificationData
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.orZero
import com.tangem.utils.extensions.stripZeroPlainString

View file

@ -6,7 +6,8 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.tangem.common.ui.notifications.NotificationUM
@ -27,7 +28,7 @@ import com.tangem.features.send.v2.subcomponents.amount.SendAmountBlockComponent
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationBlockComponent
import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent
import com.tangem.features.send.v2.subcomponents.notifications
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsComponent
import com.tangem.features.send.v2.subcomponents.notifications.DefaultSendNotificationsComponent
import kotlinx.collections.immutable.ImmutableList
private const val BLOCKS_KEY = "BLOCKS_KEY"
@ -39,7 +40,7 @@ internal fun SendConfirmContent(
destinationBlockComponent: SendDestinationBlockComponent,
amountBlockComponent: SendAmountBlockComponent,
feeBlockComponent: SendFeeBlockComponent,
notificationsComponent: NotificationsComponent,
notificationsComponent: DefaultSendNotificationsComponent,
notificationsUM: ImmutableList<NotificationUM>,
) {
val confirmUM = sendUM.confirmUM as? ConfirmUM.Content

View file

@ -16,6 +16,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.nft.component.NFTDetailsBlockComponent
import com.tangem.features.send.v2.api.SendNotificationsComponent
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.PredefinedValues
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
@ -26,8 +27,7 @@ import com.tangem.features.send.v2.subcomponents.destination.SendDestinationBloc
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationComponentParams.DestinationBlockParams
import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponentParams
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsComponent
import com.tangem.features.send.v2.subcomponents.notifications.model.NotificationData
import com.tangem.features.send.v2.subcomponents.notifications.DefaultSendNotificationsComponent
import kotlinx.coroutines.flow.*
import java.math.BigDecimal
@ -83,15 +83,15 @@ internal class NFTSendConfirmComponent(
),
)
private val notificationsComponent = NotificationsComponent(
private val notificationsComponent = DefaultSendNotificationsComponent(
appComponentContext = child("NFTSendConfirmNotifications"),
params = NotificationsComponent.Params(
params = SendNotificationsComponent.Params(
analyticsCategoryName = params.analyticsCategoryName,
userWalletId = params.userWallet.walletId,
cryptoCurrencyStatus = params.cryptoCurrencyStatus,
feeCryptoCurrencyStatus = params.feeCryptoCurrencyStatus,
appCurrency = params.appCurrency,
notificationData = NotificationData(
notificationData = SendNotificationsComponent.Params.NotificationData(
destinationAddress = model.confirmData.enteredDestination.orEmpty(),
memo = model.confirmData.enteredMemo,
amountValue = BigDecimal.ZERO,

View file

@ -26,6 +26,7 @@ import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.features.nft.entity.NFTSendSuccessTrigger
import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.SendBalanceUpdater
import com.tangem.features.send.v2.common.SendConfirmAlertFactory
@ -47,7 +48,6 @@ import com.tangem.features.send.v2.subcomponents.fee.SendFeeCheckReloadTrigger
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsUpdateTrigger
import com.tangem.features.send.v2.subcomponents.notifications.model.NotificationData
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.stripZeroPlainString
import com.tangem.utils.transformer.update

View file

@ -28,7 +28,7 @@ import com.tangem.features.send.v2.sendnft.ui.state.NFTSendUM
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationBlockComponent
import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent
import com.tangem.features.send.v2.subcomponents.notifications
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsComponent
import com.tangem.features.send.v2.subcomponents.notifications.DefaultSendNotificationsComponent
import kotlinx.collections.immutable.ImmutableList
private const val BLOCKS_KEY = "BLOCKS_KEY"
@ -40,7 +40,7 @@ internal fun NFTSendConfirmContent(
destinationBlockComponent: SendDestinationBlockComponent,
nftDetailsBlockComponent: NFTDetailsBlockComponent,
feeBlockComponent: SendFeeBlockComponent,
notificationsComponent: NotificationsComponent,
notificationsComponent: DefaultSendNotificationsComponent,
notificationsUM: ImmutableList<NotificationUM>,
) {
val confirmUM = nftSendUM.confirmUM as? ConfirmUM.Content

View file

@ -0,0 +1,45 @@
package com.tangem.features.send.v2.subcomponents.notifications
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.ui.Modifier
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.features.send.v2.api.SendNotificationsComponent
import com.tangem.features.send.v2.api.SendNotificationsComponent.Params
import com.tangem.features.send.v2.subcomponents.notifications
import com.tangem.features.send.v2.subcomponents.notifications.model.NotificationsModel
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.flow.StateFlow
internal class DefaultSendNotificationsComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted params: Params,
) : SendNotificationsComponent, AppComponentContext by appComponentContext {
private val model: NotificationsModel = getOrCreateModel(params)
override val state: StateFlow<ImmutableList<NotificationUM>> = model.uiState
override fun LazyListScope.content(
state: ImmutableList<NotificationUM>,
modifier: Modifier,
hasPaddingAbove: Boolean,
isClickDisabled: Boolean,
) {
notifications(
notifications = state,
modifier = modifier,
hasPaddingAbove = hasPaddingAbove,
isClickDisabled = isClickDisabled,
)
}
@AssistedFactory
interface Factory : SendNotificationsComponent.Factory {
override fun create(context: AppComponentContext, params: Params): DefaultSendNotificationsComponent
}
}

View file

@ -1,6 +1,6 @@
package com.tangem.features.send.v2.subcomponents.notifications
import com.tangem.features.send.v2.subcomponents.notifications.model.NotificationData
import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow

View file

@ -1,15 +0,0 @@
package com.tangem.features.send.v2.subcomponents.notifications.model
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.domain.transaction.error.GetFeeError
import java.math.BigDecimal
data class NotificationData(
val destinationAddress: String,
val memo: String?,
val amountValue: BigDecimal,
val reduceAmountBy: BigDecimal,
val isIgnoreReduce: Boolean,
val fee: Fee?,
val feeError: GetFeeError?,
)

View file

@ -35,12 +35,13 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck
import com.tangem.domain.transaction.usecase.ValidateTransactionUseCase
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.features.send.v2.api.SendNotificationsComponent
import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData
import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceTrigger
import com.tangem.features.send.v2.subcomponents.fee.SendFeeData
import com.tangem.features.send.v2.subcomponents.fee.SendFeeReloadTrigger
import com.tangem.features.send.v2.subcomponents.fee.model.checkAndCalculateSubtractedAmount
import com.tangem.features.send.v2.subcomponents.fee.model.checkFeeCoverage
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsComponent
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsUpdateTrigger
import com.tangem.features.send.v2.subcomponents.notifications.analytics.NotificationsAnalyticEvents
import com.tangem.lib.crypto.BlockchainUtils
@ -77,7 +78,7 @@ internal class NotificationsModel @Inject constructor(
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model() {
private val params: NotificationsComponent.Params = paramsContainer.require()
private val params: SendNotificationsComponent.Params = paramsContainer.require()
private val analyticsCategoryName = params.analyticsCategoryName
private val userWalletId = params.userWalletId