diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/SwapTransactionsState.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/SwapTransactionsState.kt index 5e42550ab6..d55821009b 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/SwapTransactionsState.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/SwapTransactionsState.kt @@ -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, - val hasFailed: MutableStateFlow, - val statuses: MutableStateFlow>, - val notification: MutableStateFlow = MutableStateFlow(null), + val activeStatus: ExchangeStatus?, + val hasFailed: Boolean, + val statuses: ImmutableList, + 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, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt index 2c2330ba57..39b31fb3f0 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt @@ -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, private val appCurrencyProvider: Provider, @@ -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, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt index 3650c86759..fd13879bc9 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt @@ -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 { - if (status == null) return emptyList() + private fun getStatuses(status: ExchangeStatus?, hasFailed: Boolean = false): ImmutableList { + 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( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBlock.kt index 9d44f11a9e..1574bb2db6 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBlock.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBlock.kt @@ -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>, + statuses: ImmutableList, 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, ) } } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt index f8850738e2..7cd58e238d 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt @@ -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 diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusItems.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusItems.kt index c73e3e3051..c32011f436 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusItems.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusItems.kt @@ -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 diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt index e1f2ca8766..7ef7622a82 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt @@ -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() diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index ffda8ffed0..100aabe066 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -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 = {}, ), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt index 0d4dcc4a3f..f244b3ffca 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt @@ -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") } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index a410bff92e..7d3c8f1dab 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -779,7 +779,6 @@ internal class WalletViewModel @Inject constructor( override fun onCloseSwapPromoNotificationClick() { viewModelScope.launch(dispatchers.main) { shouldShowSwapPromoWalletUseCase.neverToShow() - analyticsEventsHandler.send(WalletScreenAnalyticsEvent.SwapPromo) } }