Updated on 2026-08-14
This commit is contained in:
commit
c86f915e7d
28 changed files with 274 additions and 66 deletions
|
|
@ -0,0 +1,45 @@
|
|||
package com.tangem.common.ui.expressStatus
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
fun ColumnScope.ExpressHideButton(isTerminal: Boolean, isAutoDisposable: Boolean, onClick: () -> Unit) {
|
||||
AnimatedVisibility(
|
||||
visible = isTerminal && !isAutoDisposable,
|
||||
label = "Hide button visibility animation",
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.express_status_hide_button_text),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.button,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 12.dp, end = 12.dp, top = 14.dp)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(bounded = false),
|
||||
onClick = onClick,
|
||||
)
|
||||
.padding(vertical = 10.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -59,6 +59,11 @@ fun OnrampStatusBottomSheetContent(state: ExpressTransactionStateUM.OnrampUM) {
|
|||
SpacerH12()
|
||||
ExpressStatusBlock(state = state.info.status)
|
||||
ExpressStatusNotificationBlock(state = state.info.notification)
|
||||
ExpressHideButton(
|
||||
isTerminal = state.activeStatus.isTerminal,
|
||||
isAutoDisposable = state.activeStatus.isAutoDisposable,
|
||||
onClick = state.info.onDisposeExpressStatus,
|
||||
)
|
||||
SpacerH24()
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ data class ExpressTransactionStateInfoUM(
|
|||
val timestampFormatted: TextReference,
|
||||
val onGoToProviderClick: (String) -> Unit,
|
||||
val onClick: () -> Unit,
|
||||
val onDisposeExpressStatus: () -> Unit,
|
||||
val iconState: ExpressTransactionStateIconUM,
|
||||
val toAmount: TextReference,
|
||||
val toFiatAmount: TextReference?,
|
||||
|
|
|
|||
|
|
@ -314,6 +314,9 @@
|
|||
<string name="express_status_bought">Gekauft %s</string>
|
||||
<string name="express_status_buying">Kauf %s</string>
|
||||
<string name="express_status_buying_active">Kauf %s…</string>
|
||||
<string name="express_status_hide_button_text">Diese Transaktion ausblenden</string>
|
||||
<string name="express_status_hide_dialog_text">Sobald dieser Bildschirm ausgeblendet wurde, kann er nicht mehr aufgerufen werden.</string>
|
||||
<string name="express_status_hide_dialog_title">Bist Du sicher, dass Du die Transaktionsdetails ausblenden willst?</string>
|
||||
<string name="express_token_list_empty_search">Keine Token gefunden. Bitte versuche eine andere Anfrage</string>
|
||||
<string name="express_transaction_id">ID: %s</string>
|
||||
<string name="express_transaction_id_copied">Transaktions-ID kopiert</string>
|
||||
|
|
@ -790,6 +793,8 @@
|
|||
<string name="staking_migrate">Migrieren</string>
|
||||
<string name="staking_native">Natives Staking</string>
|
||||
<string name="staking_no_validators_error_message">Derzeit sind keine aktiven Validatoren für das Staking verfügbar. Bitte versuchen Sie es später erneut.</string>
|
||||
<string name="staking_notification_additional_ada_deposit_text">Beim Staking im Cardano Netzwerk wird das gesamte Guthaben genutzt. Zusätzlich werden 2 ADA reserviert und nach dem Unstaking zurückgegeben. Deine ADA sind während des Stakings nicht gesperrt.</string>
|
||||
<string name="staking_notification_additional_ada_deposit_title">Einzelheiten zum ADA-Staking</string>
|
||||
<string name="staking_notification_claim_rewards_text">Verdiente Belohnungen werden an Deine Wallet gesendet und stehen Dir sofort zur Verfügung</string>
|
||||
<string name="staking_notification_earn_rewards_text">Stake sicher und verdienen Belohnungen</string>
|
||||
<string name="staking_notification_earn_rewards_text_daily">Sicher staken und tägliche Belohnungen verdienen.</string>
|
||||
|
|
@ -803,6 +808,8 @@
|
|||
<string name="staking_notification_earn_rewards_title">Verdiene Staking-Belohnungen</string>
|
||||
<string name="staking_notification_low_staked_balance_text">Dein verbleibender Stakingbetrag ist zu niedrig, um den Einsatz aufzuheben. Du musst mehr Staken, um den Mindestbetrag zum Aufheben des Einsatzes zu erreichen.</string>
|
||||
<string name="staking_notification_low_staked_balance_title">Niedriges Stakingguthaben</string>
|
||||
<string name="staking_notification_minimum_balance_error_text">Für erneutes Staking sind mindestens %1$s %2$s erforderlich. Bitte lade Dein Guthaben auf.</string>
|
||||
<string name="staking_notification_minimum_balance_error_title">Nicht genug %s</string>
|
||||
<string name="staking_notification_network_error_text">Aufgrund von Netzwerkproblemen ist Staking derzeit nicht verfügbar. Bitte versuche es später erneut.</string>
|
||||
<string name="staking_notification_new_validator_funds_transfer">Beim Staking im %1$s -Netzwerk mit einem neuen Validator werden alle zuvor eingesetzten Kryptos automatisch an diesen Validator übertragen</string>
|
||||
<string name="staking_notification_restake_rewards_text">Reinvestiert Deine verdienten Prämien in Deinen Einsatzbetrag und erhöht so den potenziellen Gewinn.</string>
|
||||
|
|
|
|||
|
|
@ -648,7 +648,7 @@
|
|||
<string name="reset_card_with_backup_to_factory_message">工場出荷時設定にリセットすると、選択したカードやリングからウォレットが完全に削除されます。現在のウォレットを復元したり、カードやリングを使用してアクセスコードを復元することはできません。</string>
|
||||
<string name="reset_card_without_backup_to_factory_message">工場出荷時の状態にリセットすると、選択したカードやリングからウォレットが完全に削除され、アプリから削除されます。現在のウォレットを復元することはできません。</string>
|
||||
<string name="ring_promo_text">Tangem Ringユーザーは、11/15日までChangelly経由でスワップを3回手数料ゼロで行えます!</string>
|
||||
<string name="ring_promo_title">今すぐ手数料0%でスワップしましょう!</string>
|
||||
<string name="ring_promo_title">今すぐ手数料0% でスワップしましょう!</string>
|
||||
<string name="save_user_wallet_agreement_access_description">アプリにログインして、カードまたはリングをスキャンせずに残高を確認できます</string>
|
||||
<string name="save_user_wallet_agreement_access_title">アプリにアクセスする</string>
|
||||
<string name="save_user_wallet_agreement_allow_biometrics">生体認証の使用を許可する</string>
|
||||
|
|
@ -780,6 +780,8 @@
|
|||
<string name="staking_migrate">移行</string>
|
||||
<string name="staking_native">ネイティブステーキング</string>
|
||||
<string name="staking_no_validators_error_message">現在、ステーキングに使用できるアクティブなバリデータは見つかりません。しばらくしてからもう一度お試しください。</string>
|
||||
<string name="staking_notification_additional_ada_deposit_text">Cardanoネットワークでステーキングする場合、残高全体が使用されます。追加の2ADAが予約され、ステーキング解除後に返却されます。ステーキング中はADAはロックされません。</string>
|
||||
<string name="staking_notification_additional_ada_deposit_title">ADAステーキングの詳細</string>
|
||||
<string name="staking_notification_claim_rewards_text">獲得した報酬はあなたのアドレスに直接送られ、すぐ使用可能です。</string>
|
||||
<string name="staking_notification_earn_rewards_text">安全にステーキングして報酬を獲得しましょう</string>
|
||||
<string name="staking_notification_earn_rewards_text_daily">安全にステーキングして、報酬を毎日獲得しましょう</string>
|
||||
|
|
@ -988,7 +990,7 @@
|
|||
<string name="wallet_network_group_title">%sネットワーク</string>
|
||||
<string name="wallet_notification_address_copied">アドレスがクリップボードにコピーされました</string>
|
||||
<string name="wallet_notification_no_internet">インターネット接続がありません</string>
|
||||
<string name="wallet_promo_banner_button_title">今すぐ10 %オフで購入</string>
|
||||
<string name="wallet_promo_banner_button_title">今すぐ10 % オフで購入</string>
|
||||
<string name="wallet_promo_banner_description">1.3万種類以上の暗号資産にアクセス。ワンタップで買付、売却、スワップ、ステーキングが可能です。\nバックアップ用に最大3枚のカードを連携できます。</string>
|
||||
<string name="wallet_promo_banner_title">Tangemウォレットを見る</string>
|
||||
<string name="wallet_settings_title">ウォレット設定</string>
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@
|
|||
<string name="common_generate_addresses">Synchronize addresses</string>
|
||||
<string name="common_go_to_provider">Go to provider</string>
|
||||
<string name="common_go_to_token">Go to token</string>
|
||||
<string name="common_hide">Hide</string>
|
||||
<string name="common_hour">hour</string>
|
||||
<string name="common_import">Import</string>
|
||||
<string name="common_in_progress">In progress</string>
|
||||
|
|
@ -314,6 +315,9 @@
|
|||
<string name="express_status_bought">Bought %s</string>
|
||||
<string name="express_status_buying">Buying %s</string>
|
||||
<string name="express_status_buying_active">Buying %s...</string>
|
||||
<string name="express_status_hide_button_text">Hide this transaction</string>
|
||||
<string name="express_status_hide_dialog_text">Once hidden, the transaction status cannot be viewed again. You can simply swipe to dismiss instead.</string>
|
||||
<string name="express_status_hide_dialog_title">Hide Transaction Status?</string>
|
||||
<string name="express_token_list_empty_search">No tokens found. Please try another request</string>
|
||||
<string name="express_transaction_id">ID: %s</string>
|
||||
<string name="express_transaction_id_copied">Transaction ID copied</string>
|
||||
|
|
@ -792,6 +796,8 @@
|
|||
<string name="staking_migrate">Migrate</string>
|
||||
<string name="staking_native">Native staking</string>
|
||||
<string name="staking_no_validators_error_message">No active validators available for staking at the moment. Please try again later.</string>
|
||||
<string name="staking_notification_additional_ada_deposit_text">When staking in the Cardano network, the entire balance is used. An additional 2 ADA will be reserved and returned after unstaking. Your ADA is not locked while staking.</string>
|
||||
<string name="staking_notification_additional_ada_deposit_title">ADA Staking Details</string>
|
||||
<string name="staking_notification_claim_rewards_text">Earned rewards will be sent to your wallet and available for use immediately</string>
|
||||
<string name="staking_notification_earn_rewards_text">Stake securely and start earning your rewards</string>
|
||||
<string name="staking_notification_earn_rewards_text_daily">Stake securely and start earning daily rewards</string>
|
||||
|
|
@ -805,6 +811,8 @@
|
|||
<string name="staking_notification_earn_rewards_title">Earn staking rewards</string>
|
||||
<string name="staking_notification_low_staked_balance_text">Your remaining staked balance will be too low to unstake. You’ll need to stake more to meet the minimum unstake amount.</string>
|
||||
<string name="staking_notification_low_staked_balance_title">Low staked balance</string>
|
||||
<string name="staking_notification_minimum_balance_error_text">A minimum of %1$s %2$s is required for restaking. Please top up your balance.</string>
|
||||
<string name="staking_notification_minimum_balance_error_title">Not enough %s</string>
|
||||
<string name="staking_notification_network_error_text">Staking is currently unavailable due to network conditions. Please try again later.</string>
|
||||
<string name="staking_notification_new_validator_funds_transfer">Staking in the %1$s network with a new validator will automatically transfer all previously staked funds to this validator</string>
|
||||
<string name="staking_notification_restake_rewards_text">Reinvests your earned rewards in your staked amount, increasing potential earnings.</string>
|
||||
|
|
|
|||
|
|
@ -49,6 +49,22 @@ data class OnrampStatus(
|
|||
-> false
|
||||
}
|
||||
|
||||
val isAutoDisposable: Boolean
|
||||
get() = when (this) {
|
||||
Finished,
|
||||
-> true
|
||||
Expired,
|
||||
Failed,
|
||||
Paused,
|
||||
Created,
|
||||
WaitingForPayment,
|
||||
PaymentProcessing,
|
||||
Verifying,
|
||||
Paid,
|
||||
Sending,
|
||||
-> false
|
||||
}
|
||||
|
||||
val isHidden: Boolean
|
||||
get() = when (this) {
|
||||
Created,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.feature.swap
|
||||
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.common.currency.UserTokensResponseFactory
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectList
|
||||
|
|
@ -26,6 +27,7 @@ internal class DefaultSwapTransactionRepository(
|
|||
) : SwapTransactionRepository {
|
||||
|
||||
private val converter = SavedSwapTransactionListConverter(excludedBlockchains)
|
||||
private val userTokensResponseFactory = UserTokensResponseFactory()
|
||||
|
||||
override suspend fun storeTransaction(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
@ -33,7 +35,13 @@ internal class DefaultSwapTransactionRepository(
|
|||
toCryptoCurrency: CryptoCurrency,
|
||||
transaction: SavedSwapTransactionModel,
|
||||
) {
|
||||
transaction.status?.let { storeTransactionState(transaction.txId, it) }
|
||||
transaction.status?.let {
|
||||
storeTransactionState(
|
||||
txId = transaction.txId,
|
||||
status = it,
|
||||
refundTokenCurrency = null,
|
||||
)
|
||||
}
|
||||
appPreferencesStore.editData { mutablePreferences ->
|
||||
val savedTransactions: List<SavedSwapTransactionListModelInner>? = mutablePreferences.getObjectList(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
|
||||
|
|
@ -154,14 +162,22 @@ internal class DefaultSwapTransactionRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun storeTransactionState(txId: String, status: ExchangeStatusModel) {
|
||||
override suspend fun storeTransactionState(
|
||||
txId: String,
|
||||
status: ExchangeStatusModel,
|
||||
refundTokenCurrency: CryptoCurrency?,
|
||||
) {
|
||||
appPreferencesStore.editData { mutablePreferences ->
|
||||
val savedMap = mutablePreferences.getObjectMap<ExchangeStatusModel>(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
|
||||
)
|
||||
|
||||
val updatesMap = savedMap.toMutableMap()
|
||||
updatesMap[txId] = status
|
||||
updatesMap[txId] = status.copy(
|
||||
refundTokensResponse = refundTokenCurrency?.let {
|
||||
userTokensResponseFactory.createResponseToken(refundTokenCurrency)
|
||||
},
|
||||
)
|
||||
|
||||
mutablePreferences.setObjectMap(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
|
||||
|
|
|
|||
|
|
@ -53,7 +53,15 @@ internal class SavedSwapTransactionListConverter(
|
|||
|
||||
return SavedSwapTransactionListModel(
|
||||
transactions = value.transactions.map { tx ->
|
||||
tx.copy(status = txStatuses[tx.txId])
|
||||
val status = txStatuses[tx.txId]
|
||||
val refundCurrency = status?.refundTokensResponse?.let { id ->
|
||||
responseCryptoCurrenciesFactory.createCurrency(
|
||||
responseToken = id,
|
||||
scanResponse = scanResponse,
|
||||
)
|
||||
}
|
||||
val statusWithRefundCurrency = status?.copy(refundCurrency = refundCurrency)
|
||||
tx.copy(status = statusWithRefundCurrency)
|
||||
},
|
||||
userWalletId = value.userWalletId,
|
||||
fromCryptoCurrencyId = value.fromCryptoCurrencyId,
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ dependencies {
|
|||
/** Core modules */
|
||||
implementation(projects.core.utils)
|
||||
implementation(projects.core.ui)
|
||||
implementation(projects.core.datasource)
|
||||
|
||||
/** Feature Apis */
|
||||
implementation(projects.features.wallet.api)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package com.tangem.feature.swap.domain.models.domain
|
|||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ExchangeStatusModel(
|
||||
|
|
@ -19,6 +21,10 @@ data class ExchangeStatusModel(
|
|||
val refundNetwork: String? = null,
|
||||
@Json(name = "refundContractAddress")
|
||||
val refundContractAddress: String? = null,
|
||||
@Json(name = "refundTokensResponse")
|
||||
val refundTokensResponse: UserTokensResponse.Token? = null,
|
||||
@Json(ignore = true)
|
||||
val refundCurrency: CryptoCurrency? = null,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = false)
|
||||
|
|
@ -64,4 +70,16 @@ enum class ExchangeStatus {
|
|||
|
||||
@Json(name = "Paused")
|
||||
Paused,
|
||||
|
||||
;
|
||||
|
||||
val isTerminal: Boolean
|
||||
get() = this == Refunded ||
|
||||
this == Finished ||
|
||||
this == Cancelled ||
|
||||
this == TxFailed ||
|
||||
this == Unknown
|
||||
|
||||
val isAutoDisposable: Boolean
|
||||
get() = this == Finished
|
||||
}
|
||||
|
|
@ -30,7 +30,7 @@ interface SwapTransactionRepository {
|
|||
txId: String,
|
||||
)
|
||||
|
||||
suspend fun storeTransactionState(txId: String, status: ExchangeStatusModel)
|
||||
suspend fun storeTransactionState(txId: String, status: ExchangeStatusModel, refundTokenCurrency: CryptoCurrency?)
|
||||
|
||||
suspend fun storeLastSwappedCryptoCurrencyId(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state.components
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
|
||||
|
|
@ -133,5 +134,22 @@ internal data class TokenDetailsDialogConfig(
|
|||
onClick = onConfirmClick,
|
||||
)
|
||||
}
|
||||
|
||||
data class ConfirmExpressStatusHideDialogConfig(
|
||||
val onConfirmClick: () -> Unit,
|
||||
val onCancelClick: () -> Unit,
|
||||
) : DialogContentConfig() {
|
||||
override val title: TextReference = resourceReference(R.string.express_status_hide_dialog_title)
|
||||
override val message: TextReference = resourceReference(R.string.express_status_hide_dialog_text)
|
||||
override val confirmButtonConfig: ButtonConfig = ButtonConfig(
|
||||
text = resourceReference(R.string.common_hide),
|
||||
onClick = onConfirmClick,
|
||||
)
|
||||
|
||||
override val cancelButtonConfig: ButtonConfig = ButtonConfig(
|
||||
text = resourceReference(R.string.common_cancel),
|
||||
onClick = onCancelClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,6 @@ internal data class ExchangeUM(
|
|||
val statuses: ImmutableList<ExchangeStatusState>,
|
||||
val notification: ExchangeStatusNotifications? = null,
|
||||
val showProviderLink: Boolean,
|
||||
val isRefundTerminalStatus: Boolean,
|
||||
val fromCryptoCurrency: CryptoCurrency,
|
||||
val toCryptoCurrency: CryptoCurrency,
|
||||
) : ExpressTransactionStateUM
|
||||
|
|
@ -94,6 +94,7 @@ internal class TokenDetailsOnrampTransactionStateConverter(
|
|||
clickIntents.onGoToProviderClick(it)
|
||||
},
|
||||
onClick = { clickIntents.onExpressTransactionClick(value.txId) },
|
||||
onDisposeExpressStatus = clickIntents::onConfirmDisposeExpressStatus,
|
||||
),
|
||||
providerName = value.providerName,
|
||||
providerImageUrl = value.providerImageUrl,
|
||||
|
|
|
|||
|
|
@ -340,6 +340,22 @@ internal class TokenDetailsStateFactory(
|
|||
return balanceSelectStateConverter.convert(buttonConfig)
|
||||
}
|
||||
|
||||
fun getStateWithConfirmHideExpressStatus(): TokenDetailsState {
|
||||
return currentStateProvider().copy(
|
||||
dialogConfig = TokenDetailsDialogConfig(
|
||||
isShow = true,
|
||||
onDismissRequest = clickIntents::onDismissDialog,
|
||||
content = TokenDetailsDialogConfig.DialogContentConfig.ConfirmExpressStatusHideDialogConfig(
|
||||
onConfirmClick = {
|
||||
clickIntents.onDisposeExpressStatus()
|
||||
clickIntents.onDismissDialog()
|
||||
},
|
||||
onCancelClick = clickIntents::onDismissDialog,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun TokenDetailsAppBarMenuConfig.updateMenu(
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
hasDerivations: Boolean,
|
||||
|
|
|
|||
|
|
@ -80,17 +80,20 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
fromFiatAmount = quote.fiatRate.multiply(fromAmount)
|
||||
}
|
||||
}
|
||||
val notifications =
|
||||
getNotification(transaction.status?.status, transaction.status?.txExternalUrl, null)
|
||||
val statusModel = transaction.status
|
||||
val notifications = getNotification(
|
||||
status = statusModel?.status,
|
||||
txUrl = statusModel?.txExternalUrl,
|
||||
refundToken = statusModel?.refundCurrency,
|
||||
)
|
||||
val showProviderLink = getShowProviderLink(notifications, transaction.status)
|
||||
result.add(
|
||||
ExchangeUM(
|
||||
provider = transaction.provider,
|
||||
statuses = getStatuses(transaction.status?.status),
|
||||
statuses = getStatuses(statusModel?.status),
|
||||
notification = notifications,
|
||||
activeStatus = transaction.status?.status,
|
||||
activeStatus = statusModel?.status,
|
||||
showProviderLink = showProviderLink,
|
||||
isRefundTerminalStatus = true,
|
||||
fromCryptoCurrency = fromCryptoCurrency,
|
||||
toCryptoCurrency = toCryptoCurrency,
|
||||
info = createStateInfo(
|
||||
|
|
@ -107,25 +110,19 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
return result.toPersistentList()
|
||||
}
|
||||
|
||||
fun updateTxStatus(
|
||||
tx: ExchangeUM,
|
||||
statusModel: ExchangeStatusModel?,
|
||||
refundToken: CryptoCurrency?,
|
||||
isRefundTerminalStatus: Boolean,
|
||||
): ExchangeUM {
|
||||
fun updateTxStatus(tx: ExchangeUM, statusModel: ExchangeStatusModel?): ExchangeUM {
|
||||
if (statusModel == null || tx.activeStatus == statusModel.status) {
|
||||
Timber.e("UpdateTxStatus isn't required. Current status isn't changed")
|
||||
return tx
|
||||
}
|
||||
val hasFailed = statusModel.status == ExchangeStatus.Failed
|
||||
val notifications = getNotification(statusModel.status, statusModel.txExternalUrl, refundToken)
|
||||
val notifications = getNotification(statusModel.status, statusModel.txExternalUrl, statusModel.refundCurrency)
|
||||
val showProviderLink = getShowProviderLink(notifications, statusModel)
|
||||
return tx.copy(
|
||||
activeStatus = statusModel.status,
|
||||
notification = notifications,
|
||||
statuses = getStatuses(statusModel.status, hasFailed),
|
||||
showProviderLink = showProviderLink,
|
||||
isRefundTerminalStatus = isRefundTerminalStatus,
|
||||
info = tx.info.copy(txExternalUrl = statusModel.txExternalUrl),
|
||||
)
|
||||
}
|
||||
|
|
@ -164,7 +161,8 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
},
|
||||
iconState = getIconState(transaction.status?.status),
|
||||
status = getStatusState(),
|
||||
notification = null, // fixme [REDACTED_JIRA]
|
||||
notification = null, // fixme [REDACTED_JIRA],
|
||||
onDisposeExpressStatus = clickIntents::onConfirmDisposeExpressStatus,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,13 +79,13 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun removeTransactionOnBottomSheetClosed(isForceTerminal: Boolean = false) {
|
||||
suspend fun removeTransactionOnBottomSheetClosed(isForceDispose: Boolean = false) {
|
||||
val state = currentStateProvider()
|
||||
val bottomSheetConfig = state.bottomSheetConfig?.content as? ExpressStatusBottomSheetConfig ?: return
|
||||
val selectedTx = bottomSheetConfig.value as? ExchangeUM ?: return
|
||||
|
||||
val shouldTerminate = selectedTx.activeStatus.isTerminal(selectedTx.isRefundTerminalStatus) || isForceTerminal
|
||||
if (shouldTerminate) {
|
||||
val shouldDispose = selectedTx.activeStatus?.isAutoDisposable == true || isForceDispose
|
||||
if (shouldDispose) {
|
||||
swapTransactionRepository.removeTransaction(
|
||||
userWalletId = userWalletId,
|
||||
fromCryptoCurrency = selectedTx.fromCryptoCurrency,
|
||||
|
|
@ -96,20 +96,14 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
|||
}
|
||||
|
||||
suspend fun updateSwapTxStatus(swapTx: ExchangeUM): ExchangeUM {
|
||||
return if (swapTx.activeStatus.isTerminal(swapTx.isRefundTerminalStatus)) {
|
||||
return if (swapTx.activeStatus?.isTerminal == true) {
|
||||
swapTx
|
||||
} else {
|
||||
val statusModel = getExchangeStatus(swapTx.info.txId, swapTx.provider)
|
||||
val isRefundTerminalStatus = statusModel?.refundNetwork == null &&
|
||||
statusModel?.refundContractAddress == null &&
|
||||
swapTx.provider.type != ExchangeProviderType.DEX_BRIDGE
|
||||
|
||||
val addedRefundToken = addRefundCurrencyIfNeeded(statusModel, swapTx.provider.type)
|
||||
swapTransactionsStateConverter.updateTxStatus(
|
||||
tx = swapTx,
|
||||
statusModel = statusModel,
|
||||
refundToken = addedRefundToken,
|
||||
isRefundTerminalStatus = isRefundTerminalStatus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -120,8 +114,11 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
|||
ifLeft = { null },
|
||||
ifRight = { statusModel ->
|
||||
sendStatusUpdateAnalytics(statusModel, provider)
|
||||
swapTransactionRepository.storeTransactionState(txId, statusModel)
|
||||
statusModel
|
||||
|
||||
val refundTokenCurrency = addRefundCurrencyIfNeeded(statusModel, provider.type)
|
||||
|
||||
swapTransactionRepository.storeTransactionState(txId, statusModel, refundTokenCurrency)
|
||||
statusModel.copy(refundCurrency = refundTokenCurrency)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -174,15 +171,6 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun ExchangeStatus?.isTerminal(isRefundTerminal: Boolean): Boolean {
|
||||
val needTerminalRefund = this == ExchangeStatus.Refunded && isRefundTerminal
|
||||
return needTerminalRefund ||
|
||||
this == ExchangeStatus.Finished ||
|
||||
this == ExchangeStatus.Cancelled ||
|
||||
this == ExchangeStatus.TxFailed ||
|
||||
this == ExchangeStatus.Unknown
|
||||
}
|
||||
|
||||
private fun toAnalyticStatus(status: ExchangeStatus?): ExpressAnalyticsStatus? {
|
||||
return when (status) {
|
||||
ExchangeStatus.New,
|
||||
|
|
|
|||
|
|
@ -157,11 +157,13 @@ internal class ExpressStatusFactory @AssistedInject constructor(
|
|||
|
||||
suspend fun removeTransactionOnBottomSheetClosed(
|
||||
expressState: ExpressTransactionStateUM,
|
||||
isForceTerminal: Boolean = false,
|
||||
isForceDispose: Boolean = false,
|
||||
) {
|
||||
when (expressState) {
|
||||
is ExchangeUM -> exchangeStatusFactory.removeTransactionOnBottomSheetClosed(isForceTerminal)
|
||||
is ExpressTransactionStateUM.OnrampUM -> onrampStatusFactory.removeTransactionOnBottomSheetClosed()
|
||||
is ExchangeUM -> exchangeStatusFactory.removeTransactionOnBottomSheetClosed(isForceDispose)
|
||||
is ExpressTransactionStateUM.OnrampUM -> onrampStatusFactory.removeTransactionOnBottomSheetClosed(
|
||||
isForceDispose,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,12 +68,12 @@ internal class OnrampStatusFactory @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun removeTransactionOnBottomSheetClosed() {
|
||||
suspend fun removeTransactionOnBottomSheetClosed(isForceDispose: Boolean) {
|
||||
val state = currentStateProvider()
|
||||
val bottomSheetConfig = state.bottomSheetConfig?.content as? ExpressStatusBottomSheetConfig ?: return
|
||||
val selectedTx = bottomSheetConfig.value as? ExpressTransactionStateUM.OnrampUM ?: return
|
||||
|
||||
if (selectedTx.activeStatus.isTerminal) {
|
||||
if (selectedTx.activeStatus.isAutoDisposable || isForceDispose) {
|
||||
onrampRemoveTransactionUseCase(externalTxId = selectedTx.info.txExternalId)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import com.tangem.common.ui.expressStatus.ExpressEstimate
|
||||
import com.tangem.common.ui.expressStatus.ExpressHideButton
|
||||
import com.tangem.common.ui.expressStatus.ExpressProvider
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerH10
|
||||
|
|
@ -70,6 +71,11 @@ internal fun ExchangeStatusBottomSheetContent(state: ExchangeUM) {
|
|||
if (state.notification != null) {
|
||||
Notification(state = state.notification, activeStatus = state.activeStatus)
|
||||
}
|
||||
ExpressHideButton(
|
||||
isTerminal = state.activeStatus?.isTerminal == true,
|
||||
isAutoDisposable = state.activeStatus?.isAutoDisposable == true,
|
||||
onClick = state.info.onDisposeExpressStatus,
|
||||
)
|
||||
SpacerH24()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,4 +72,8 @@ interface TokenDetailsClickIntents {
|
|||
fun onGoToRefundedTokenClick(cryptoCurrency: CryptoCurrency)
|
||||
|
||||
fun onOpenUrlClick(url: String)
|
||||
|
||||
fun onConfirmDisposeExpressStatus()
|
||||
|
||||
fun onDisposeExpressStatus()
|
||||
}
|
||||
|
|
@ -784,16 +784,6 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onGoToRefundedTokenClick(cryptoCurrency: CryptoCurrency) {
|
||||
val bottomSheetState = internalUiState.value.bottomSheetConfig?.content
|
||||
if (bottomSheetState is ExpressStatusBottomSheetConfig) {
|
||||
viewModelScope.launch {
|
||||
expressStatusFactory.removeTransactionOnBottomSheetClosed(
|
||||
expressState = bottomSheetState.value,
|
||||
isForceTerminal = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
internalUiState.value = stateFactory.getStateWithClosedBottomSheet()
|
||||
router.openTokenDetails(userWalletId, cryptoCurrency)
|
||||
}
|
||||
|
||||
|
|
@ -950,6 +940,23 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
internalUiState.value = stateFactory.getStateWithUpdatedBalanceSegmentedButtonConfig(config)
|
||||
}
|
||||
|
||||
override fun onConfirmDisposeExpressStatus() {
|
||||
internalUiState.value = stateFactory.getStateWithConfirmHideExpressStatus()
|
||||
}
|
||||
|
||||
override fun onDisposeExpressStatus() {
|
||||
val bottomSheetState = internalUiState.value.bottomSheetConfig?.content
|
||||
if (bottomSheetState is ExpressStatusBottomSheetConfig) {
|
||||
viewModelScope.launch {
|
||||
expressStatusFactory.removeTransactionOnBottomSheetClosed(
|
||||
expressState = bottomSheetState.value,
|
||||
isForceDispose = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
internalUiState.value = stateFactory.getStateWithClosedBottomSheet()
|
||||
}
|
||||
|
||||
private fun handleUnavailabilityReason(
|
||||
unavailabilityReason: ScenarioUnavailabilityReason,
|
||||
tokenSymbol: String,
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ internal class OnrampStatusFactory @Inject constructor(
|
|||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
suspend fun removeTransactionOnBottomSheetClosed() {
|
||||
suspend fun removeTransactionOnBottomSheetClosed(forceDispose: Boolean) {
|
||||
val state = stateHolder.getSelectedWallet()
|
||||
val bottomSheetConfig = state.bottomSheetConfig?.content as? ExpressStatusBottomSheetConfig ?: return
|
||||
val selectedTx = bottomSheetConfig.value as? ExpressTransactionStateUM.OnrampUM ?: return
|
||||
|
||||
if (selectedTx.activeStatus.isTerminal) {
|
||||
if (selectedTx.activeStatus.isAutoDisposable || forceDispose) {
|
||||
onrampRemoveTransactionUseCase(externalTxId = selectedTx.info.txExternalId)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ internal sealed interface WalletAlertState {
|
|||
open val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
|
||||
open val isWarningConfirmButton: Boolean = false
|
||||
abstract val onConfirmClick: (() -> Unit)?
|
||||
open val cancelButtonText: TextReference? = null
|
||||
open val onCancelClick: (() -> Unit)? = null
|
||||
}
|
||||
|
||||
@Immutable
|
||||
|
|
@ -108,4 +110,14 @@ internal sealed interface WalletAlertState {
|
|||
|
||||
override val onConfirmClick: (() -> Unit)? = null
|
||||
}
|
||||
|
||||
data class ConfirmExpressStatusHide(
|
||||
override val onConfirmClick: (() -> Unit),
|
||||
override val onCancelClick: (() -> Unit),
|
||||
) : Basic() {
|
||||
override val title: TextReference = resourceReference(R.string.express_status_hide_dialog_title)
|
||||
override val message: TextReference = resourceReference(R.string.express_status_hide_dialog_text)
|
||||
override val confirmButtonText: TextReference = resourceReference(R.string.common_hide)
|
||||
override val cancelButtonText: TextReference = resourceReference(R.string.common_cancel)
|
||||
}
|
||||
}
|
||||
|
|
@ -62,9 +62,7 @@ internal class SingleWalletOnrampTransactionConverter(
|
|||
value.timestamp.toTimeFormat(),
|
||||
),
|
||||
),
|
||||
toAmount = stringReference(
|
||||
value.toAmount.format { crypto(currency) },
|
||||
),
|
||||
toAmount = stringReference(value.toAmount.format { crypto(currency) }),
|
||||
toFiatAmount = stringReference(
|
||||
status.fiatRate?.multiply(value.toAmount).format {
|
||||
fiat(
|
||||
|
|
@ -94,6 +92,7 @@ internal class SingleWalletOnrampTransactionConverter(
|
|||
analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider)
|
||||
clickIntents.onGoToProviderClick(it)
|
||||
},
|
||||
onDisposeExpressStatus = clickIntents::onConfirmDisposeExpressStatus,
|
||||
onClick = {
|
||||
val analyticEvent = TokenOnrampAnalyticsEvent.OnrampStatusOpened(
|
||||
tokenSymbol = currency.symbol,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ internal class WalletEventSender @Inject constructor(
|
|||
stateHolder.update(transformer = SendEventTransformer(event = event, onConsume = ::onConsume))
|
||||
}
|
||||
|
||||
private fun onConsume() {
|
||||
fun onConsume() {
|
||||
stateHolder.update {
|
||||
it.copy(event = consumedEvent())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,12 +15,14 @@ import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
|||
import com.tangem.feature.wallet.presentation.wallet.domain.OnrampStatusFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.unwrap
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.ActionsBottomSheetConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletBottomSheetConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.OpenBottomSheetTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCurrencyActionsConverter
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
|
@ -54,6 +56,10 @@ internal interface WalletContentClickIntents {
|
|||
fun onGoToProviderClick(externalTxId: String)
|
||||
|
||||
fun onExpressTransactionClick(txId: String)
|
||||
|
||||
fun onConfirmDisposeExpressStatus()
|
||||
|
||||
fun onDisposeExpressStatus()
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -70,6 +76,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
private val shouldShowMarketsTooltipUseCase: ShouldShowMarketsTooltipUseCase,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val walletEventSender: WalletEventSender,
|
||||
) : BaseWalletClickIntents(), WalletContentClickIntents {
|
||||
|
||||
override fun onBackClick() = router.popBackStack()
|
||||
|
|
@ -185,7 +192,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
if (stateHolder.getSelectedWallet().bottomSheetConfig?.content is ExpressStatusBottomSheetConfig) {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
onrampStatusFactory.removeTransactionOnBottomSheetClosed()
|
||||
onrampStatusFactory.removeTransactionOnBottomSheetClosed(forceDispose = false)
|
||||
}
|
||||
}
|
||||
stateHolder.update(CloseBottomSheetTransformer(userWalletId))
|
||||
|
|
@ -211,4 +218,28 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfirmDisposeExpressStatus() {
|
||||
walletEventSender.send(
|
||||
WalletEvent.ShowAlert(
|
||||
WalletAlertState.ConfirmExpressStatusHide(
|
||||
onConfirmClick = {
|
||||
walletEventSender.onConsume()
|
||||
onDisposeExpressStatus()
|
||||
},
|
||||
onCancelClick = walletEventSender::onConsume,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDisposeExpressStatus() {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
if (stateHolder.getSelectedWallet().bottomSheetConfig?.content is ExpressStatusBottomSheetConfig) {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
onrampStatusFactory.removeTransactionOnBottomSheetClosed(forceDispose = true)
|
||||
}
|
||||
}
|
||||
stateHolder.update(CloseBottomSheetTransformer(userWalletId))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue