Updated on 2026-08-14
This commit is contained in:
commit
7fcc618dfb
10 changed files with 62 additions and 47 deletions
|
|
@ -1,12 +1,13 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.currency.tokenicon.TokenIconState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
|
||||
import com.tangem.feature.swap.domain.models.domain.SwapProvider
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotifications
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal data class SwapTransactionsState(
|
||||
val txId: String,
|
||||
|
|
@ -14,10 +15,10 @@ internal data class SwapTransactionsState(
|
|||
val txUrl: String? = null,
|
||||
val timestamp: TextReference,
|
||||
val fiatSymbol: String,
|
||||
val activeStatus: MutableStateFlow<ExchangeStatus?>,
|
||||
val hasFailed: MutableStateFlow<Boolean>,
|
||||
val statuses: MutableStateFlow<List<ExchangeStatusState>>,
|
||||
val notification: MutableStateFlow<ExchangeStatusNotifications?> = MutableStateFlow(null),
|
||||
val activeStatus: ExchangeStatus?,
|
||||
val hasFailed: Boolean,
|
||||
val statuses: ImmutableList<ExchangeStatusState>,
|
||||
val notification: ExchangeStatusNotifications? = null,
|
||||
val toCryptoCurrencyId: CryptoCurrency.ID,
|
||||
val toCryptoAmount: String,
|
||||
val toCryptoSymbol: String,
|
||||
|
|
@ -32,6 +33,7 @@ internal data class SwapTransactionsState(
|
|||
val onGoToProviderClick: (String) -> Unit,
|
||||
)
|
||||
|
||||
@Immutable
|
||||
internal class ExchangeStatusState(
|
||||
val status: ExchangeStatus,
|
||||
val text: TextReference,
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
internal class TokenDetailsStateFactory(
|
||||
private val currentStateProvider: Provider<TokenDetailsState>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
|
|
@ -247,6 +248,19 @@ internal class TokenDetailsStateFactory(
|
|||
)
|
||||
}
|
||||
|
||||
fun updateStateWithExchangeStatusBottomSheet(swapTxState: SwapTransactionsState): TangemBottomSheetConfig? {
|
||||
val state = currentStateProvider()
|
||||
val bottomSheetConfig = state.bottomSheetConfig
|
||||
val currentConfig = bottomSheetConfig?.content as? ExchangeStatusBottomSheetConfig ?: return bottomSheetConfig
|
||||
return bottomSheetConfig.copy(
|
||||
content = if (currentConfig.value != swapTxState) {
|
||||
ExchangeStatusBottomSheetConfig(swapTxState)
|
||||
} else {
|
||||
currentConfig
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun getStateAndTriggerEvent(
|
||||
state: TokenDetailsState,
|
||||
errorMessage: TextReference,
|
||||
|
|
|
|||
|
|
@ -20,11 +20,10 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.component
|
|||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class TokenDetailsSwapTransactionsStateConverter(
|
||||
|
|
@ -70,14 +69,12 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
txUrl = transaction.status?.txUrl,
|
||||
timestamp = TextReference.Str("${timestamp.toDateFormat()}, ${timestamp.toTimeFormat()}"),
|
||||
fiatSymbol = appCurrency.symbol,
|
||||
statuses = MutableStateFlow(getStatuses(transaction.status?.status)),
|
||||
hasFailed = MutableStateFlow(transaction.status?.status == ExchangeStatus.Failed),
|
||||
activeStatus = MutableStateFlow(transaction.status?.status),
|
||||
notification = MutableStateFlow(
|
||||
getNotification(
|
||||
transaction.status?.status,
|
||||
transaction.status?.txUrl,
|
||||
),
|
||||
statuses = getStatuses(transaction.status?.status),
|
||||
hasFailed = transaction.status?.status == ExchangeStatus.Failed,
|
||||
activeStatus = transaction.status?.status,
|
||||
notification = getNotification(
|
||||
transaction.status?.status,
|
||||
transaction.status?.txUrl,
|
||||
),
|
||||
toCryptoCurrencyId = toCurrency.currency.id,
|
||||
toCryptoAmount = BigDecimalFormatter.formatCryptoAmount(
|
||||
|
|
@ -109,13 +106,16 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
return result.toPersistentList()
|
||||
}
|
||||
|
||||
fun updateTxStatus(tx: SwapTransactionsState, statusModel: ExchangeStatusModel?) {
|
||||
if (statusModel == null || tx.activeStatus.value == statusModel.status) return
|
||||
val hasFailed = tx.hasFailed.value || statusModel.status == ExchangeStatus.Failed
|
||||
tx.activeStatus.update { statusModel.status }
|
||||
tx.hasFailed.update { hasFailed }
|
||||
tx.notification.update { getNotification(statusModel.status, statusModel.txUrl) }
|
||||
tx.statuses.update { getStatuses(statusModel.status, hasFailed) }
|
||||
fun updateTxStatus(tx: SwapTransactionsState, statusModel: ExchangeStatusModel?): SwapTransactionsState {
|
||||
if (statusModel == null || tx.activeStatus == statusModel.status) return tx
|
||||
val hasFailed = tx.hasFailed || statusModel.status == ExchangeStatus.Failed
|
||||
return tx.copy(
|
||||
activeStatus = statusModel.status,
|
||||
hasFailed = hasFailed,
|
||||
notification = getNotification(statusModel.status, statusModel.txUrl),
|
||||
statuses = getStatuses(statusModel.status, hasFailed),
|
||||
txUrl = statusModel.txUrl,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getFiatAmount(toFiatAmount: BigDecimal): String {
|
||||
|
|
@ -149,8 +149,8 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getStatuses(status: ExchangeStatus?, hasFailed: Boolean = false): List<ExchangeStatusState> {
|
||||
if (status == null) return emptyList()
|
||||
private fun getStatuses(status: ExchangeStatus?, hasFailed: Boolean = false): ImmutableList<ExchangeStatusState> {
|
||||
if (status == null) return persistentListOf()
|
||||
val isWaiting = status == ExchangeStatus.New || status == ExchangeStatus.Waiting
|
||||
val isConfirming = status == ExchangeStatus.Confirming
|
||||
val isVerifying = status == ExchangeStatus.Verifying
|
||||
|
|
@ -181,7 +181,7 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
isRefunded = isRefunded,
|
||||
hasFailed = hasFailed,
|
||||
),
|
||||
)
|
||||
).toPersistentList()
|
||||
}
|
||||
|
||||
private fun waitStep(isNew: Boolean, isNewDone: Boolean) = ExchangeStatusState(
|
||||
|
|
|
|||
|
|
@ -18,23 +18,21 @@ import androidx.compose.ui.draw.clip
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.ExchangeStatusState
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
internal fun ExchangeStatusBlock(
|
||||
statuses: MutableStateFlow<List<ExchangeStatusState>>,
|
||||
statuses: ImmutableList<ExchangeStatusState>,
|
||||
showLink: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val statusValues = statuses.collectAsStateWithLifecycle()
|
||||
Column(
|
||||
modifier = modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
|
|
@ -77,10 +75,10 @@ internal fun ExchangeStatusBlock(
|
|||
}
|
||||
}
|
||||
|
||||
statusValues.value.forEachIndexed { index, item ->
|
||||
statuses.forEachIndexed { index, item ->
|
||||
ExchangeStatusStep(
|
||||
stepStatus = item,
|
||||
isLast = index == statusValues.value.lastIndex,
|
||||
isLast = index == statuses.lastIndex,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerH10
|
||||
import com.tangem.core.ui.components.SpacerH12
|
||||
|
|
@ -36,8 +35,6 @@ internal fun ExchangeStatusBottomSheet(config: TangemBottomSheetConfig) {
|
|||
@Composable
|
||||
private fun ExchangeStatusBottomSheetContent(content: ExchangeStatusBottomSheetConfig) {
|
||||
val config = content.value
|
||||
val status = config.activeStatus.collectAsStateWithLifecycle()
|
||||
val notification = config.notification.collectAsStateWithLifecycle()
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
|
|
@ -77,15 +74,15 @@ private fun ExchangeStatusBottomSheetContent(content: ExchangeStatusBottomSheetC
|
|||
SpacerH12()
|
||||
ExchangeStatusBlock(
|
||||
statuses = config.statuses,
|
||||
showLink = notification.value == null && config.txUrl != null,
|
||||
showLink = config.notification == null && config.txUrl != null,
|
||||
onClick = { config.onGoToProviderClick(config.txUrl.orEmpty()) },
|
||||
)
|
||||
AnimatedContent(
|
||||
targetState = notification.value,
|
||||
targetState = config.notification,
|
||||
label = "Exchange Status Notification Change",
|
||||
) {
|
||||
it?.let {
|
||||
val tint = when (status.value) {
|
||||
val tint = when (config.activeStatus) {
|
||||
ExchangeStatus.Verifying -> TangemTheme.colors.icon.attention
|
||||
ExchangeStatus.Failed -> TangemTheme.colors.icon.warning
|
||||
else -> null
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.constraintlayout.compose.*
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.ui.components.atoms.text.EllipsisText
|
||||
import com.tangem.core.ui.components.atoms.text.TextEllipsis
|
||||
import com.tangem.core.ui.components.currency.tokenicon.TokenIcon
|
||||
|
|
@ -41,8 +40,7 @@ internal fun LazyListScope.swapTransactionsItems(
|
|||
contentType = { swapTxs[it]::class.java },
|
||||
) {
|
||||
val item = swapTxs[it]
|
||||
val status = item.activeStatus.collectAsStateWithLifecycle()
|
||||
val (iconRes, tint) = when (status.value) {
|
||||
val (iconRes, tint) = when (item.activeStatus) {
|
||||
ExchangeStatus.Verifying -> R.drawable.ic_alert_triangle_20 to TangemTheme.colors.icon.attention
|
||||
ExchangeStatus.Failed -> R.drawable.ic_alert_circle_24 to TangemTheme.colors.icon.warning
|
||||
else -> null to null
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
|||
import com.tangem.domain.tokens.models.analytics.TokenExchangeAnalyticsEvent
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
import com.tangem.feature.swap.domain.api.SwapRepository
|
||||
import com.tangem.feature.swap.domain.SwapTransactionRepository
|
||||
import com.tangem.feature.swap.domain.api.SwapRepository
|
||||
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
|
||||
import com.tangem.feature.swap.domain.models.domain.ExchangeStatusModel
|
||||
import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionListModel
|
||||
|
|
@ -81,8 +81,9 @@ internal class ExchangeStatusFactory(
|
|||
swapTxList.map { tx ->
|
||||
async {
|
||||
val statusModel = getExchangeStatus(tx.txId)
|
||||
swapTransactionsStateConverter.updateTxStatus(tx, statusModel)
|
||||
tx.removeIfFinished(statusModel?.status)
|
||||
swapTransactionsStateConverter
|
||||
.updateTxStatus(tx, statusModel)
|
||||
.removeIfFinished(statusModel?.status)
|
||||
}
|
||||
}
|
||||
.awaitAll()
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRout
|
|||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTransactionsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsStateFactory
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.exchange.ExchangeStatusBottomSheetConfig
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
import com.tangem.utils.coroutines.*
|
||||
|
|
@ -229,7 +230,14 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
},
|
||||
onSuccess = { updatedTxs ->
|
||||
uiState = uiState.copy(swapTxs = updatedTxs)
|
||||
val config = uiState.bottomSheetConfig?.content as? ExchangeStatusBottomSheetConfig
|
||||
val currentTx = updatedTxs.firstOrNull { it.txId == config?.value?.txId }
|
||||
uiState = uiState.copy(
|
||||
swapTxs = updatedTxs,
|
||||
bottomSheetConfig = currentTx?.let(
|
||||
stateFactory::updateStateWithExchangeStatusBottomSheet,
|
||||
),
|
||||
)
|
||||
},
|
||||
onError = {},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -63,6 +63,4 @@ sealed class WalletScreenAnalyticsEvent {
|
|||
object NoticeWalletLocked : MainScreen(event = "Notice - Wallet Locked")
|
||||
object WalletUnlockTapped : MainScreen(event = "Button - Wallet Unlock Tapped")
|
||||
}
|
||||
|
||||
object SwapPromo : AnalyticsEvent(category = "Swap Promo", event = "Button - Close")
|
||||
}
|
||||
|
|
@ -779,7 +779,6 @@ internal class WalletViewModel @Inject constructor(
|
|||
override fun onCloseSwapPromoNotificationClick() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
shouldShowSwapPromoWalletUseCase.neverToShow()
|
||||
analyticsEventsHandler.send(WalletScreenAnalyticsEvent.SwapPromo)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue