Updated on 2026-08-14
This commit is contained in:
parent
f6b98ab9fa
commit
db16ba6cef
20 changed files with 618 additions and 96 deletions
|
|
@ -98,7 +98,7 @@ class TangemLoggingInitializer(
|
||||||
BlockchainSdkConfig.serializer(),
|
BlockchainSdkConfig.serializer(),
|
||||||
environmentConfig.blockchainSdkConfig,
|
environmentConfig.blockchainSdkConfig,
|
||||||
)
|
)
|
||||||
// Drop URL-shaped values (e.g. public endpoint URLs from BlockchainSdkConfig like
|
// Drop URL-shaped drawable (e.g. public endpoint URLs from BlockchainSdkConfig like
|
||||||
// kaspaSecondaryApiUrl); they are not secrets and would obscure unrelated requests in logs.
|
// kaspaSecondaryApiUrl); they are not secrets and would obscure unrelated requests in logs.
|
||||||
val values = JsonStringValuesExtractor.extract(json)
|
val values = JsonStringValuesExtractor.extract(json)
|
||||||
.filter { it.isNotBlank() && !it.startsWith("http", ignoreCase = true) }
|
.filter { it.isNotBlank() && !it.startsWith("http", ignoreCase = true) }
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<paths>
|
<paths>
|
||||||
<files-path name="files" path="/"/>
|
<files-path name="files" path="/"/>
|
||||||
|
<cache-path name="shared_images" path="." />
|
||||||
</paths>
|
</paths>
|
||||||
|
|
@ -2,9 +2,9 @@ package com.tangem.common.ui.expressStatus
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
|
||||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||||
|
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
|
||||||
data class ExpressStatusBottomSheetConfig(
|
data class ExpressStatusBottomSheetConfig(
|
||||||
|
|
@ -18,7 +18,7 @@ fun ExpressStatusBottomSheet(config: TangemBottomSheetConfig) {
|
||||||
containerColor = TangemTheme.colors.background.tertiary,
|
containerColor = TangemTheme.colors.background.tertiary,
|
||||||
) { content: ExpressStatusBottomSheetConfig ->
|
) { content: ExpressStatusBottomSheetConfig ->
|
||||||
when (val state = content.value) {
|
when (val state = content.value) {
|
||||||
is ExpressTransactionStateUM.OnrampUM -> OnrampStatusBottomSheetContent(state)
|
is ExpressTransactionStateUM.OnrampUM -> OnrampStatusBottomSheetContent(state, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,13 +3,7 @@ package com.tangem.common.ui.expressStatus
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.lazy.LazyListScope
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
|
@ -22,11 +16,7 @@ import androidx.compose.ui.platform.testTag
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import com.tangem.common.ui.R
|
import com.tangem.common.ui.R
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressLinkUM
|
import com.tangem.common.ui.expressStatus.state.*
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressStatusUM
|
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateIconUM
|
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM
|
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
|
||||||
import com.tangem.core.ui.components.atoms.text.EllipsisText
|
import com.tangem.core.ui.components.atoms.text.EllipsisText
|
||||||
import com.tangem.core.ui.components.atoms.text.TextEllipsis
|
import com.tangem.core.ui.components.atoms.text.TextEllipsis
|
||||||
import com.tangem.core.ui.components.currency.icon.CurrencyIcon
|
import com.tangem.core.ui.components.currency.icon.CurrencyIcon
|
||||||
|
|
@ -221,13 +211,17 @@ private val PreviewExpressTransactionState: ExpressTransactionStateUM = object :
|
||||||
onDisposeExpressStatus = {},
|
onDisposeExpressStatus = {},
|
||||||
iconState = ExpressTransactionStateIconUM.None,
|
iconState = ExpressTransactionStateIconUM.None,
|
||||||
toAmount = stringReference("0,11441958 BTC"),
|
toAmount = stringReference("0,11441958 BTC"),
|
||||||
|
toAmountValue = "0.11441958".toBigDecimal(),
|
||||||
toFiatAmount = null,
|
toFiatAmount = null,
|
||||||
toAmountSymbol = "BTC",
|
toAmountSymbol = "BTC",
|
||||||
toCurrencyIcon = CurrencyIconState.Loading,
|
toCurrencyIcon = CurrencyIconState.Loading,
|
||||||
|
toAddress = "0x",
|
||||||
fromAmount = stringReference("100 SOL"),
|
fromAmount = stringReference("100 SOL"),
|
||||||
|
fromAmountValue = "100".toBigDecimal(),
|
||||||
fromFiatAmount = null,
|
fromFiatAmount = null,
|
||||||
fromAmountSymbol = "SOL",
|
fromAmountSymbol = "SOL",
|
||||||
fromCurrencyIcon = CurrencyIconState.Loading,
|
fromCurrencyIcon = CurrencyIconState.Loading,
|
||||||
|
fromAddress = "0x",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
@ -9,18 +9,16 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||||
import com.tangem.core.ui.R
|
import com.tangem.core.ui.R
|
||||||
import com.tangem.core.ui.components.SpacerH10
|
import com.tangem.core.ui.components.*
|
||||||
import com.tangem.core.ui.components.SpacerH12
|
|
||||||
import com.tangem.core.ui.components.SpacerH16
|
|
||||||
import com.tangem.core.ui.components.SpacerH24
|
|
||||||
import com.tangem.core.ui.extensions.stringReference
|
import com.tangem.core.ui.extensions.stringReference
|
||||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun OnrampStatusBottomSheetContent(state: ExpressTransactionStateUM.OnrampUM) {
|
fun OnrampStatusBottomSheetContent(state: ExpressTransactionStateUM.OnrampUM, isExpressShareButtonEnabled: Boolean) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||||
|
|
@ -70,6 +68,10 @@ fun OnrampStatusBottomSheetContent(state: ExpressTransactionStateUM.OnrampUM) {
|
||||||
isAutoDisposable = state.activeStatus.isAutoDisposable,
|
isAutoDisposable = state.activeStatus.isAutoDisposable,
|
||||||
onClick = state.info.onDisposeExpressStatus,
|
onClick = state.info.onDisposeExpressStatus,
|
||||||
)
|
)
|
||||||
|
if (isExpressShareButtonEnabled) {
|
||||||
|
SpacerH(80.dp)
|
||||||
|
} else {
|
||||||
SpacerH24()
|
SpacerH24()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
package com.tangem.common.ui.expressStatus.state
|
package com.tangem.common.ui.expressStatus.state
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
import com.tangem.common.ui.notifications.NotificationUM
|
import com.tangem.common.ui.notifications.NotificationUM
|
||||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
import com.tangem.domain.onramp.model.OnrampStatus
|
import com.tangem.domain.onramp.model.OnrampStatus
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
@Immutable
|
||||||
interface ExpressTransactionStateUM {
|
interface ExpressTransactionStateUM {
|
||||||
|
|
||||||
val info: ExpressTransactionStateInfoUM
|
val info: ExpressTransactionStateInfoUM
|
||||||
|
|
@ -35,13 +38,17 @@ data class ExpressTransactionStateInfoUM(
|
||||||
val onDisposeExpressStatus: () -> Unit,
|
val onDisposeExpressStatus: () -> Unit,
|
||||||
val iconState: ExpressTransactionStateIconUM,
|
val iconState: ExpressTransactionStateIconUM,
|
||||||
val toAmount: TextReference,
|
val toAmount: TextReference,
|
||||||
|
val toAmountValue: BigDecimal,
|
||||||
val toFiatAmount: TextReference?,
|
val toFiatAmount: TextReference?,
|
||||||
val toAmountSymbol: String,
|
val toAmountSymbol: String,
|
||||||
val toCurrencyIcon: CurrencyIconState,
|
val toCurrencyIcon: CurrencyIconState,
|
||||||
|
val toAddress: String,
|
||||||
val fromAmount: TextReference,
|
val fromAmount: TextReference,
|
||||||
|
val fromAmountValue: BigDecimal,
|
||||||
val fromFiatAmount: TextReference?,
|
val fromFiatAmount: TextReference?,
|
||||||
val fromAmountSymbol: String,
|
val fromAmountSymbol: String,
|
||||||
val fromCurrencyIcon: CurrencyIconState,
|
val fromCurrencyIcon: CurrencyIconState,
|
||||||
|
val fromAddress: String?,
|
||||||
) {
|
) {
|
||||||
val subtitle: TextReference
|
val subtitle: TextReference
|
||||||
get() = buildExpressStatusSubtitle(activeStatus = activeStatus, date = timestampAgoFormatted)
|
get() = buildExpressStatusSubtitle(activeStatus = activeStatus, date = timestampAgoFormatted)
|
||||||
|
|
|
||||||
|
|
@ -138,5 +138,9 @@
|
||||||
{
|
{
|
||||||
"name": "TWI_83_ADDRESS_BOOK_ENABLED",
|
"name": "TWI_83_ADDRESS_BOOK_ENABLED",
|
||||||
"version": "undefined"
|
"version": "undefined"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AND_15489_EXPRESS_SHARE_BUTTON_ENABLED",
|
||||||
|
"version": "undefined"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,5 @@ interface SwapFeatureToggles {
|
||||||
val isSwapProviderFilterEnabled: Boolean
|
val isSwapProviderFilterEnabled: Boolean
|
||||||
val isSwapRateExperienceEnabled: Boolean
|
val isSwapRateExperienceEnabled: Boolean
|
||||||
val isSwapPredefinedButtonsEnabled: Boolean
|
val isSwapPredefinedButtonsEnabled: Boolean
|
||||||
|
val isExpressShareButtonEnabled: Boolean
|
||||||
}
|
}
|
||||||
|
|
@ -6,34 +6,45 @@ import com.tangem.features.swap.SwapFeatureToggles
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class DefaultSwapFeatureToggles @Inject constructor(
|
internal class DefaultSwapFeatureToggles @Inject constructor(
|
||||||
featureTogglesManager: FeatureTogglesManager,
|
private val featureTogglesManager: FeatureTogglesManager,
|
||||||
) : SwapFeatureToggles {
|
) : SwapFeatureToggles {
|
||||||
|
|
||||||
override val isYieldSwapEnabled: Boolean = featureTogglesManager.isFeatureEnabled(
|
override val isYieldSwapEnabled: Boolean
|
||||||
|
get() = featureTogglesManager.isFeatureEnabled(
|
||||||
toggle = FeatureToggles.TWI_1326_YIELD_MODE_SWAP_ENABLED,
|
toggle = FeatureToggles.TWI_1326_YIELD_MODE_SWAP_ENABLED,
|
||||||
)
|
)
|
||||||
|
|
||||||
override val isSwapSwitchToTransferEnabled: Boolean = featureTogglesManager.isFeatureEnabled(
|
override val isSwapSwitchToTransferEnabled: Boolean
|
||||||
|
get() = featureTogglesManager.isFeatureEnabled(
|
||||||
toggle = FeatureToggles.AND_15207_SWAP_SWITCH_TO_TRANSFER_ENABLED,
|
toggle = FeatureToggles.AND_15207_SWAP_SWITCH_TO_TRANSFER_ENABLED,
|
||||||
)
|
)
|
||||||
|
|
||||||
override val isSwapIntegratedApproveEnabled: Boolean = featureTogglesManager.isFeatureEnabled(
|
override val isSwapIntegratedApproveEnabled: Boolean
|
||||||
|
get() = featureTogglesManager.isFeatureEnabled(
|
||||||
toggle = FeatureToggles.AND_15120_SWAP_INTEGRATED_APPROVE,
|
toggle = FeatureToggles.AND_15120_SWAP_INTEGRATED_APPROVE,
|
||||||
)
|
)
|
||||||
|
|
||||||
override val isSwapAbEnabled: Boolean = featureTogglesManager.isFeatureEnabled(
|
override val isSwapAbEnabled: Boolean
|
||||||
|
get() = featureTogglesManager.isFeatureEnabled(
|
||||||
toggle = FeatureToggles.SWAP_AB_ENABLED,
|
toggle = FeatureToggles.SWAP_AB_ENABLED,
|
||||||
)
|
)
|
||||||
|
|
||||||
override val isSwapProviderFilterEnabled: Boolean = featureTogglesManager.isFeatureEnabled(
|
override val isSwapProviderFilterEnabled: Boolean
|
||||||
|
get() = featureTogglesManager.isFeatureEnabled(
|
||||||
toggle = FeatureToggles.AND_15009_SWAP_PROVIDER_FILTER_ENABLED,
|
toggle = FeatureToggles.AND_15009_SWAP_PROVIDER_FILTER_ENABLED,
|
||||||
)
|
)
|
||||||
|
|
||||||
override val isSwapRateExperienceEnabled: Boolean = featureTogglesManager.isFeatureEnabled(
|
override val isSwapRateExperienceEnabled: Boolean
|
||||||
|
get() = featureTogglesManager.isFeatureEnabled(
|
||||||
toggle = FeatureToggles.AND_15103_SWAP_RATE_EXPERIENCE_ENABLED,
|
toggle = FeatureToggles.AND_15103_SWAP_RATE_EXPERIENCE_ENABLED,
|
||||||
)
|
)
|
||||||
|
|
||||||
override val isSwapPredefinedButtonsEnabled: Boolean = featureTogglesManager.isFeatureEnabled(
|
override val isSwapPredefinedButtonsEnabled: Boolean
|
||||||
|
get() = featureTogglesManager.isFeatureEnabled(
|
||||||
toggle = FeatureToggles.AND_15122_SWAP_PREDEFINED_BUTTONS_ENABLED,
|
toggle = FeatureToggles.AND_15122_SWAP_PREDEFINED_BUTTONS_ENABLED,
|
||||||
)
|
)
|
||||||
|
override val isExpressShareButtonEnabled: Boolean
|
||||||
|
get() = featureTogglesManager.isFeatureEnabled(
|
||||||
|
toggle = FeatureToggles.AND_15489_EXPRESS_SHARE_BUTTON_ENABLED,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -3,14 +3,7 @@ package com.tangem.features.tangempay.components.express
|
||||||
import androidx.compose.foundation.lazy.LazyListScope
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import com.tangem.common.ui.expressStatus.expressTransactionsItemsLegacy
|
import com.tangem.common.ui.expressStatus.expressTransactionsItemsLegacy
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressLinkUM
|
import com.tangem.common.ui.expressStatus.state.*
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressStatusItemState
|
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressStatusItemUM
|
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressStatusUM
|
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateIconUM
|
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM
|
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionsBlockState
|
|
||||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
import com.tangem.domain.onramp.model.OnrampStatus
|
import com.tangem.domain.onramp.model.OnrampStatus
|
||||||
|
|
@ -122,13 +115,17 @@ internal class PreviewEmptyExpressTransactionsComponent : ExpressTransactionsCom
|
||||||
onDisposeExpressStatus = {},
|
onDisposeExpressStatus = {},
|
||||||
iconState = iconState,
|
iconState = iconState,
|
||||||
toAmount = TextReference.Str(toAmount),
|
toAmount = TextReference.Str(toAmount),
|
||||||
|
toAmountValue = toAmount.toBigDecimal(),
|
||||||
toFiatAmount = null,
|
toFiatAmount = null,
|
||||||
toAmountSymbol = toSymbol,
|
toAmountSymbol = toSymbol,
|
||||||
toCurrencyIcon = CurrencyIconState.Empty(),
|
toCurrencyIcon = CurrencyIconState.Empty(),
|
||||||
|
toAddress = "",
|
||||||
fromAmount = TextReference.Str(fromAmount),
|
fromAmount = TextReference.Str(fromAmount),
|
||||||
|
fromAmountValue = fromAmount.toBigDecimal(),
|
||||||
fromFiatAmount = null,
|
fromFiatAmount = null,
|
||||||
fromAmountSymbol = fromSymbol,
|
fromAmountSymbol = fromSymbol,
|
||||||
fromCurrencyIcon = CurrencyIconState.Empty(),
|
fromCurrencyIcon = CurrencyIconState.Empty(),
|
||||||
|
fromAddress = "",
|
||||||
),
|
),
|
||||||
providerName = "Preview Provider",
|
providerName = "Preview Provider",
|
||||||
providerImageUrl = "",
|
providerImageUrl = "",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
|
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
|
||||||
|
|
||||||
|
import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||||
import com.tangem.common.ui.expressStatus.state.*
|
import com.tangem.common.ui.expressStatus.state.*
|
||||||
import com.tangem.common.ui.expressStatus.toActiveStatusText
|
import com.tangem.common.ui.expressStatus.toActiveStatusText
|
||||||
import com.tangem.common.ui.expressStatus.toIconState
|
import com.tangem.common.ui.expressStatus.toIconState
|
||||||
|
|
@ -7,7 +8,6 @@ import com.tangem.common.ui.notifications.ExpressNotificationsUM
|
||||||
import com.tangem.common.ui.notifications.NotificationUM
|
import com.tangem.common.ui.notifications.NotificationUM
|
||||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||||
import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
import com.tangem.core.ui.extensions.stringReference
|
import com.tangem.core.ui.extensions.stringReference
|
||||||
import com.tangem.core.ui.extensions.wrappedList
|
import com.tangem.core.ui.extensions.wrappedList
|
||||||
|
|
@ -41,6 +41,7 @@ internal class TokenDetailsOnrampTransactionStateConverter(
|
||||||
|
|
||||||
override fun convert(value: OnrampTransaction): ExpressTransactionStateUM.OnrampUM {
|
override fun convert(value: OnrampTransaction): ExpressTransactionStateUM.OnrampUM {
|
||||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||||
|
val statusValue = cryptoCurrencyStatus?.value
|
||||||
val appCurrency = appCurrencyProvider()
|
val appCurrency = appCurrencyProvider()
|
||||||
return ExpressTransactionStateUM.OnrampUM(
|
return ExpressTransactionStateUM.OnrampUM(
|
||||||
info = ExpressTransactionStateInfoUM(
|
info = ExpressTransactionStateInfoUM(
|
||||||
|
|
@ -63,8 +64,9 @@ internal class TokenDetailsOnrampTransactionStateConverter(
|
||||||
toAmount = stringReference(
|
toAmount = stringReference(
|
||||||
value.toAmount.format { crypto(cryptoCurrency) },
|
value.toAmount.format { crypto(cryptoCurrency) },
|
||||||
),
|
),
|
||||||
|
toAmountValue = value.toAmount,
|
||||||
toFiatAmount = stringReference(
|
toFiatAmount = stringReference(
|
||||||
cryptoCurrencyStatus?.value?.fiatRate?.multiply(value.toAmount).format {
|
statusValue?.fiatRate?.multiply(value.toAmount).format {
|
||||||
fiat(
|
fiat(
|
||||||
fiatCurrencyCode = appCurrency.code,
|
fiatCurrencyCode = appCurrency.code,
|
||||||
fiatCurrencySymbol = appCurrency.symbol,
|
fiatCurrencySymbol = appCurrency.symbol,
|
||||||
|
|
@ -73,6 +75,7 @@ internal class TokenDetailsOnrampTransactionStateConverter(
|
||||||
),
|
),
|
||||||
toAmountSymbol = cryptoCurrency.symbol,
|
toAmountSymbol = cryptoCurrency.symbol,
|
||||||
toCurrencyIcon = iconStateConverter.convert(cryptoCurrency),
|
toCurrencyIcon = iconStateConverter.convert(cryptoCurrency),
|
||||||
|
toAddress = statusValue?.networkAddress?.defaultAddress?.value.orEmpty(),
|
||||||
fromAmount = stringReference(
|
fromAmount = stringReference(
|
||||||
value.fromAmount.format {
|
value.fromAmount.format {
|
||||||
fiat(
|
fiat(
|
||||||
|
|
@ -81,12 +84,14 @@ internal class TokenDetailsOnrampTransactionStateConverter(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
fromAmountValue = value.fromAmount,
|
||||||
fromFiatAmount = null,
|
fromFiatAmount = null,
|
||||||
fromAmountSymbol = value.fromCurrency.code,
|
fromAmountSymbol = value.fromCurrency.code,
|
||||||
fromCurrencyIcon = CurrencyIconState.FiatIcon(
|
fromCurrencyIcon = CurrencyIconState.FiatIcon(
|
||||||
url = value.fromCurrency.image,
|
url = value.fromCurrency.image,
|
||||||
fallbackResId = R.drawable.ic_currency_24,
|
fallbackResId = R.drawable.ic_currency_24,
|
||||||
),
|
),
|
||||||
|
fromAddress = null,
|
||||||
iconState = value.status.toIconState(),
|
iconState = value.status.toIconState(),
|
||||||
onGoToProviderClick = { url ->
|
onGoToProviderClick = { url ->
|
||||||
analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider())
|
analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider())
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
|
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
|
||||||
|
|
||||||
|
import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressLinkUM
|
import com.tangem.common.ui.expressStatus.state.ExpressLinkUM
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressStatusUM
|
import com.tangem.common.ui.expressStatus.state.ExpressStatusUM
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateIconUM
|
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateIconUM
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM
|
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM
|
||||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||||
import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
import com.tangem.core.ui.extensions.stringReference
|
import com.tangem.core.ui.extensions.stringReference
|
||||||
|
|
@ -17,9 +17,9 @@ import com.tangem.core.ui.utils.mapFormattedDate
|
||||||
import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday
|
import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday
|
||||||
import com.tangem.core.ui.utils.toTimeFormat
|
import com.tangem.core.ui.utils.toTimeFormat
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
|
import com.tangem.domain.models.account.Account
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.quote.QuoteStatus
|
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||||
import com.tangem.domain.models.quote.mapData
|
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent
|
import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent
|
||||||
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
|
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
|
||||||
|
|
@ -34,11 +34,11 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.E
|
||||||
import com.tangem.features.tokendetails.impl.R
|
import com.tangem.features.tokendetails.impl.R
|
||||||
import com.tangem.utils.Provider
|
import com.tangem.utils.Provider
|
||||||
import com.tangem.utils.converter.Converter
|
import com.tangem.utils.converter.Converter
|
||||||
|
import com.tangem.utils.logging.TangemLogger
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.PersistentList
|
import kotlinx.collections.immutable.PersistentList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
import com.tangem.utils.logging.TangemLogger
|
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
||||||
// Fixme [REDACTED_JIRA]
|
// Fixme [REDACTED_JIRA]
|
||||||
|
|
@ -59,7 +59,7 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
||||||
|
|
||||||
fun convert(
|
fun convert(
|
||||||
savedTransactions: List<SavedSwapTransactionListModel>,
|
savedTransactions: List<SavedSwapTransactionListModel>,
|
||||||
quoteStatuses: Set<QuoteStatus>,
|
accountStatuses: Map<Account, CryptoCurrencyStatus>,
|
||||||
): PersistentList<ExchangeUM> {
|
): PersistentList<ExchangeUM> {
|
||||||
val result = mutableListOf<ExchangeUM>()
|
val result = mutableListOf<ExchangeUM>()
|
||||||
|
|
||||||
|
|
@ -69,23 +69,25 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
||||||
val fromCryptoCurrency = swapTransaction.fromCryptoCurrency
|
val fromCryptoCurrency = swapTransaction.fromCryptoCurrency
|
||||||
val toCryptoCurrencyRawId = swapTransaction.toCryptoCurrency.id.rawCurrencyId
|
val toCryptoCurrencyRawId = swapTransaction.toCryptoCurrency.id.rawCurrencyId
|
||||||
val fromCryptoCurrencyRawId = swapTransaction.fromCryptoCurrency.id.rawCurrencyId
|
val fromCryptoCurrencyRawId = swapTransaction.fromCryptoCurrency.id.rawCurrencyId
|
||||||
|
var fromCryptoCurrencyStatus: CryptoCurrencyStatus? = null
|
||||||
|
var toCryptoCurrencyStatus: CryptoCurrencyStatus? = null
|
||||||
swapTransaction.transactions.forEach { transaction ->
|
swapTransaction.transactions.forEach { transaction ->
|
||||||
val toAmount = transaction.toCryptoAmount
|
val toAmount = transaction.toCryptoAmount
|
||||||
val fromAmount = transaction.fromCryptoAmount
|
val fromAmount = transaction.fromCryptoAmount
|
||||||
var toFiatAmount: BigDecimal? = null
|
var toFiatAmount: BigDecimal? = null
|
||||||
var fromFiatAmount: BigDecimal? = null
|
var fromFiatAmount: BigDecimal? = null
|
||||||
quoteStatuses.forEach { quote ->
|
accountStatuses.forEach { (account, cryptoCurrencyStatus) ->
|
||||||
quote.mapData {
|
if (cryptoCurrencyStatus.currency.id.rawCurrencyId == fromCryptoCurrencyRawId &&
|
||||||
if (quote.rawCurrencyId == toCryptoCurrencyRawId) {
|
account.userWalletId.stringValue == swapTransaction.fromUserWalletId
|
||||||
toFiatAmount = fiatRate.multiply(toAmount)
|
) {
|
||||||
}
|
fromFiatAmount = cryptoCurrencyStatus.value.fiatRate?.multiply(fromAmount)
|
||||||
}
|
fromCryptoCurrencyStatus = cryptoCurrencyStatus
|
||||||
|
|
||||||
quote.mapData {
|
|
||||||
if (quote.rawCurrencyId == fromCryptoCurrencyRawId) {
|
|
||||||
fromFiatAmount = fiatRate.multiply(fromAmount)
|
|
||||||
}
|
}
|
||||||
|
if (cryptoCurrencyStatus.currency.id.rawCurrencyId == toCryptoCurrencyRawId &&
|
||||||
|
account.userWalletId.stringValue == swapTransaction.toUserWalletId
|
||||||
|
) {
|
||||||
|
toFiatAmount = cryptoCurrencyStatus.value.fiatRate?.multiply(toAmount)
|
||||||
|
toCryptoCurrencyStatus = cryptoCurrencyStatus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val statusModel = transaction.status
|
val statusModel = transaction.status
|
||||||
|
|
@ -111,6 +113,8 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
||||||
transaction = transaction,
|
transaction = transaction,
|
||||||
toCryptoCurrency = toCryptoCurrency,
|
toCryptoCurrency = toCryptoCurrency,
|
||||||
fromCryptoCurrency = fromCryptoCurrency,
|
fromCryptoCurrency = fromCryptoCurrency,
|
||||||
|
fromCryptoCurrencyStatus = fromCryptoCurrencyStatus,
|
||||||
|
toCryptoCurrencyStatus = toCryptoCurrencyStatus,
|
||||||
toFiatAmount = toFiatAmount,
|
toFiatAmount = toFiatAmount,
|
||||||
fromFiatAmount = fromFiatAmount,
|
fromFiatAmount = fromFiatAmount,
|
||||||
),
|
),
|
||||||
|
|
@ -149,14 +153,19 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("LongParameterList")
|
||||||
private fun createStateInfo(
|
private fun createStateInfo(
|
||||||
transaction: SavedSwapTransactionModel,
|
transaction: SavedSwapTransactionModel,
|
||||||
toCryptoCurrency: CryptoCurrency,
|
toCryptoCurrency: CryptoCurrency,
|
||||||
fromCryptoCurrency: CryptoCurrency,
|
fromCryptoCurrency: CryptoCurrency,
|
||||||
|
fromCryptoCurrencyStatus: CryptoCurrencyStatus?,
|
||||||
|
toCryptoCurrencyStatus: CryptoCurrencyStatus?,
|
||||||
toFiatAmount: BigDecimal?,
|
toFiatAmount: BigDecimal?,
|
||||||
fromFiatAmount: BigDecimal?,
|
fromFiatAmount: BigDecimal?,
|
||||||
): ExpressTransactionStateInfoUM {
|
): ExpressTransactionStateInfoUM {
|
||||||
val timestamp = transaction.timestamp
|
val timestamp = transaction.timestamp
|
||||||
|
val fromStatusValue = fromCryptoCurrencyStatus?.value
|
||||||
|
val toStatusValue = toCryptoCurrencyStatus?.value
|
||||||
return ExpressTransactionStateInfoUM(
|
return ExpressTransactionStateInfoUM(
|
||||||
title = resourceReference(R.string.express_exchange_by, wrappedList(transaction.provider.name)),
|
title = resourceReference(R.string.express_exchange_by, wrappedList(transaction.provider.name)),
|
||||||
txId = transaction.txId,
|
txId = transaction.txId,
|
||||||
|
|
@ -169,13 +178,17 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
||||||
timestampAgoFormatted = mapFormattedDate(timestamp),
|
timestampAgoFormatted = mapFormattedDate(timestamp),
|
||||||
activeStatus = getActiveStatusText(transaction.status?.status),
|
activeStatus = getActiveStatusText(transaction.status?.status),
|
||||||
toAmount = getCryptoAmount(transaction.toCryptoAmount, toCryptoCurrency),
|
toAmount = getCryptoAmount(transaction.toCryptoAmount, toCryptoCurrency),
|
||||||
|
toAmountValue = transaction.toCryptoAmount,
|
||||||
toFiatAmount = getFiatAmount(toFiatAmount),
|
toFiatAmount = getFiatAmount(toFiatAmount),
|
||||||
toCurrencyIcon = iconStateConverter.convert(toCryptoCurrency),
|
toCurrencyIcon = iconStateConverter.convert(toCryptoCurrency),
|
||||||
toAmountSymbol = toCryptoCurrency.symbol,
|
toAmountSymbol = toCryptoCurrency.symbol,
|
||||||
|
toAddress = toStatusValue?.networkAddress?.defaultAddress?.value.orEmpty(),
|
||||||
fromAmount = getCryptoAmount(transaction.fromCryptoAmount, fromCryptoCurrency),
|
fromAmount = getCryptoAmount(transaction.fromCryptoAmount, fromCryptoCurrency),
|
||||||
|
fromAmountValue = transaction.fromCryptoAmount,
|
||||||
fromFiatAmount = getFiatAmount(fromFiatAmount),
|
fromFiatAmount = getFiatAmount(fromFiatAmount),
|
||||||
fromCurrencyIcon = iconStateConverter.convert(fromCryptoCurrency),
|
fromCurrencyIcon = iconStateConverter.convert(fromCryptoCurrency),
|
||||||
fromAmountSymbol = fromCryptoCurrency.symbol,
|
fromAmountSymbol = fromCryptoCurrency.symbol,
|
||||||
|
fromAddress = fromStatusValue?.networkAddress?.defaultAddress?.value.orEmpty(),
|
||||||
onClick = { clickIntents.onExpressTransactionClick(transaction.txId) },
|
onClick = { clickIntents.onExpressTransactionClick(transaction.txId) },
|
||||||
onGoToProviderClick = { url ->
|
onGoToProviderClick = { url ->
|
||||||
analyticsEventsHandler.send(
|
analyticsEventsHandler.send(
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,13 @@ import com.tangem.datasource.local.swap.SwapTransactionStatusStore
|
||||||
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
|
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
|
||||||
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
|
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
|
import com.tangem.domain.models.account.Account
|
||||||
import com.tangem.domain.models.account.AccountId
|
import com.tangem.domain.models.account.AccountId
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.quote.QuoteStatus
|
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.quotes.QuotesRepository
|
import com.tangem.domain.pay.usecase.GetPaymentAccountCryptoCurrencyStatusUseCase
|
||||||
import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent
|
import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent
|
||||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||||
import com.tangem.feature.swap.domain.SwapTransactionRepository
|
import com.tangem.feature.swap.domain.SwapTransactionRepository
|
||||||
|
|
@ -37,8 +38,8 @@ import kotlinx.coroutines.flow.map
|
||||||
internal class ExchangeStatusFactory @AssistedInject constructor(
|
internal class ExchangeStatusFactory @AssistedInject constructor(
|
||||||
private val swapTransactionRepository: SwapTransactionRepository,
|
private val swapTransactionRepository: SwapTransactionRepository,
|
||||||
private val swapRepository: SwapRepository,
|
private val swapRepository: SwapRepository,
|
||||||
private val quotesRepository: QuotesRepository,
|
|
||||||
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
|
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
|
||||||
|
private val getPaymentAccountCryptoCurrencyStatusUseCase: GetPaymentAccountCryptoCurrencyStatusUseCase,
|
||||||
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
|
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
|
||||||
private val swapTransactionStatusStore: SwapTransactionStatusStore,
|
private val swapTransactionStatusStore: SwapTransactionStatusStore,
|
||||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||||
|
|
@ -62,19 +63,23 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
||||||
operator fun invoke(): Flow<PersistentList<ExchangeUM>> {
|
operator fun invoke(): Flow<PersistentList<ExchangeUM>> {
|
||||||
return swapTransactionRepository.getTransactions(
|
return swapTransactionRepository.getTransactions(
|
||||||
userWallet = userWallet,
|
userWallet = userWallet,
|
||||||
|
|
||||||
cryptoCurrencyId = cryptoCurrency.id,
|
cryptoCurrencyId = cryptoCurrency.id,
|
||||||
).conflate()
|
).conflate()
|
||||||
.map { savedTransactions ->
|
.map { savedTransactions ->
|
||||||
val quotes = savedTransactions
|
val accountStatuses = savedTransactions
|
||||||
?.flatMap { setOf(it.fromCryptoCurrency.id, it.toCryptoCurrency.id) }
|
?.flatMap { swapTransaction ->
|
||||||
?.toSet()
|
setOf(
|
||||||
?.getQuotesOrEmpty()
|
swapTransaction.fromAccount to swapTransaction.fromCryptoCurrency,
|
||||||
?: emptySet()
|
swapTransaction.toAccount to swapTransaction.toCryptoCurrency,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
?.toMap()
|
||||||
|
?.getStatuses()
|
||||||
|
.orEmpty()
|
||||||
|
|
||||||
getExchangeStatusState(
|
getExchangeStatusState(
|
||||||
savedTransactions = savedTransactions,
|
savedTransactions = savedTransactions,
|
||||||
quoteStatuses = quotes,
|
accountStatuses = accountStatuses,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -194,7 +199,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
||||||
|
|
||||||
private fun getExchangeStatusState(
|
private fun getExchangeStatusState(
|
||||||
savedTransactions: List<SavedSwapTransactionListModel>?,
|
savedTransactions: List<SavedSwapTransactionListModel>?,
|
||||||
quoteStatuses: Set<QuoteStatus>,
|
accountStatuses: Map<Account, CryptoCurrencyStatus>,
|
||||||
): PersistentList<ExchangeUM> {
|
): PersistentList<ExchangeUM> {
|
||||||
if (savedTransactions == null) {
|
if (savedTransactions == null) {
|
||||||
return persistentListOf()
|
return persistentListOf()
|
||||||
|
|
@ -202,7 +207,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
||||||
|
|
||||||
return swapTransactionsStateConverter.convert(
|
return swapTransactionsStateConverter.convert(
|
||||||
savedTransactions = savedTransactions,
|
savedTransactions = savedTransactions,
|
||||||
quoteStatuses = quoteStatuses,
|
accountStatuses = accountStatuses,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -226,12 +231,24 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun Set<CryptoCurrency.ID>.getQuotesOrEmpty(): Set<QuoteStatus> {
|
private suspend fun Map<Account?, CryptoCurrency>.getStatuses(): Map<Account, CryptoCurrencyStatus> {
|
||||||
val rawIds = mapNotNull { it.rawCurrencyId }.toSet()
|
return mapNotNull { (account, cryptoCurrency) ->
|
||||||
|
when (account) {
|
||||||
|
is Account.CryptoPortfolio -> {
|
||||||
|
val (cryptoPortfolioAccount, currencyStatus) = getAccountCurrencyStatusUseCase.invokeSync(
|
||||||
|
userWalletId = account.userWalletId,
|
||||||
|
currency = cryptoCurrency,
|
||||||
|
).getOrNull() ?: return@mapNotNull null
|
||||||
|
|
||||||
return runCatching { quotesRepository.getMultiQuoteSyncOrNull(currenciesIds = rawIds) }
|
cryptoPortfolioAccount to currencyStatus
|
||||||
.getOrNull()
|
}
|
||||||
.orEmpty()
|
is Account.Payment -> getPaymentAccountCryptoCurrencyStatusUseCase.invokeSync(
|
||||||
|
userWalletId = account.userWalletId,
|
||||||
|
cryptoCurrency = cryptoCurrency,
|
||||||
|
).getOrNull()
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}.toMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.model.ExpressTr
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotification
|
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotification
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
|
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusBottomSheet
|
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusBottomSheet
|
||||||
|
import com.tangem.features.swap.SwapFeatureToggles
|
||||||
import com.tangem.utils.Provider
|
import com.tangem.utils.Provider
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
|
|
@ -45,6 +46,7 @@ internal class ExpressStatusFactory @AssistedInject constructor(
|
||||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||||
onrampStatusFactory: OnrampStatusFactory.Factory,
|
onrampStatusFactory: OnrampStatusFactory.Factory,
|
||||||
exchangeStatusFactory: ExchangeStatusFactory.Factory,
|
exchangeStatusFactory: ExchangeStatusFactory.Factory,
|
||||||
|
private val swapFeatureToggles: SwapFeatureToggles,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val exchangeStatusFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
|
private val exchangeStatusFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
|
||||||
|
|
@ -212,6 +214,7 @@ internal class ExpressStatusFactory @AssistedInject constructor(
|
||||||
is ExpressStatusBottomSheetConfig -> ExpressStatusBottomSheet(
|
is ExpressStatusBottomSheetConfig -> ExpressStatusBottomSheet(
|
||||||
config = this,
|
config = this,
|
||||||
extraContent = extraContent,
|
extraContent = extraContent,
|
||||||
|
isExpressShareButtonEnabled = swapFeatureToggles.isExpressShareButtonEnabled,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express
|
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||||
import com.tangem.common.ui.expressStatus.OnrampStatusBottomSheetContent
|
import com.tangem.common.ui.expressStatus.OnrampStatusBottomSheetContent
|
||||||
import com.tangem.common.ui.expressStatus.state.*
|
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
import com.tangem.core.ui.components.BottomFade
|
||||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||||
|
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
|
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
|
||||||
|
|
@ -17,15 +21,44 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.e
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ExpressStatusBottomSheet(
|
internal fun ExpressStatusBottomSheet(
|
||||||
config: TangemBottomSheetConfig,
|
config: TangemBottomSheetConfig,
|
||||||
|
isExpressShareButtonEnabled: Boolean,
|
||||||
extraContent: (@Composable () -> Unit)? = null,
|
extraContent: (@Composable () -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
TangemBottomSheet(
|
TangemBottomSheet(
|
||||||
config = config,
|
config = config,
|
||||||
containerColor = TangemTheme.colors.background.tertiary,
|
containerColor = TangemTheme.colors.background.tertiary,
|
||||||
) { content: ExpressStatusBottomSheetConfig ->
|
) { content: ExpressStatusBottomSheetConfig ->
|
||||||
|
if (isExpressShareButtonEnabled) {
|
||||||
|
Box {
|
||||||
when (val state = content.value) {
|
when (val state = content.value) {
|
||||||
is ExpressTransactionStateUM.OnrampUM -> OnrampStatusBottomSheetContent(state)
|
is ExpressTransactionStateUM.OnrampUM -> OnrampStatusBottomSheetContent(
|
||||||
is ExchangeUM -> ExchangeStatusBottomSheetContent(state = state, extraContent = extraContent)
|
state = state,
|
||||||
|
isExpressShareButtonEnabled = true,
|
||||||
|
)
|
||||||
|
is ExchangeUM -> ExchangeStatusBottomSheetContent(
|
||||||
|
state = state,
|
||||||
|
extraContent = extraContent,
|
||||||
|
isExpressShareButtonEnabled = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
BottomFade(
|
||||||
|
backgroundColor = TangemTheme.colors.background.tertiary,
|
||||||
|
modifier = Modifier.align(Alignment.BottomCenter),
|
||||||
|
)
|
||||||
|
ExpressShareContent(state = content.value)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
when (val state = content.value) {
|
||||||
|
is ExpressTransactionStateUM.OnrampUM -> OnrampStatusBottomSheetContent(
|
||||||
|
state = state,
|
||||||
|
isExpressShareButtonEnabled = false,
|
||||||
|
)
|
||||||
|
is ExchangeUM -> ExchangeStatusBottomSheetContent(
|
||||||
|
state = state,
|
||||||
|
extraContent = extraContent,
|
||||||
|
isExpressShareButtonEnabled = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -43,6 +76,7 @@ private fun PreviewExpressStatusBottomSheet(
|
||||||
onDismissRequest = {},
|
onDismissRequest = {},
|
||||||
content = param,
|
content = param,
|
||||||
),
|
),
|
||||||
|
isExpressShareButtonEnabled = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -24,7 +24,7 @@ class ExpressStatusBottomSheetStateProvider : PreviewParameterProvider<ExpressSt
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
ExpressStatusBottomSheetConfig(
|
ExpressStatusBottomSheetConfig(
|
||||||
ExchangeUM(
|
ExchangeUM(
|
||||||
fromUserWalletId = UserWalletId("AnyUserWalletID"),
|
fromUserWalletId = UserWalletId("0123456789ABCDEF"),
|
||||||
info = ExpressTransactionStateInfoUM(
|
info = ExpressTransactionStateInfoUM(
|
||||||
title = TextReference.Str("Transaction Status"),
|
title = TextReference.Str("Transaction Status"),
|
||||||
status = ExpressStatusUM(
|
status = ExpressStatusUM(
|
||||||
|
|
@ -49,13 +49,17 @@ class ExpressStatusBottomSheetStateProvider : PreviewParameterProvider<ExpressSt
|
||||||
onDisposeExpressStatus = {},
|
onDisposeExpressStatus = {},
|
||||||
iconState = ExpressTransactionStateIconUM.None,
|
iconState = ExpressTransactionStateIconUM.None,
|
||||||
toAmount = TextReference.Str("0.1 BTC"),
|
toAmount = TextReference.Str("0.1 BTC"),
|
||||||
|
toAmountValue = "0.1".toBigDecimal(),
|
||||||
toFiatAmount = TextReference.Str("$5000"),
|
toFiatAmount = TextReference.Str("$5000"),
|
||||||
toAmountSymbol = "BTC",
|
toAmountSymbol = "BTC",
|
||||||
toCurrencyIcon = CurrencyIconState.Empty(),
|
toCurrencyIcon = CurrencyIconState.Empty(),
|
||||||
|
toAddress = "0x",
|
||||||
fromAmount = TextReference.Str("5000 USDT"),
|
fromAmount = TextReference.Str("5000 USDT"),
|
||||||
|
fromAmountValue = "5000".toBigDecimal(),
|
||||||
fromFiatAmount = TextReference.Str("$5000"),
|
fromFiatAmount = TextReference.Str("$5000"),
|
||||||
fromAmountSymbol = "USDT",
|
fromAmountSymbol = "USDT",
|
||||||
fromCurrencyIcon = CurrencyIconState.Empty(),
|
fromCurrencyIcon = CurrencyIconState.Empty(),
|
||||||
|
fromAddress = "0x",
|
||||||
),
|
),
|
||||||
provider = SwapProvider(
|
provider = SwapProvider(
|
||||||
providerId = "1",
|
providerId = "1",
|
||||||
|
|
@ -67,14 +71,20 @@ class ExpressStatusBottomSheetStateProvider : PreviewParameterProvider<ExpressSt
|
||||||
privacyPolicy = null,
|
privacyPolicy = null,
|
||||||
slippage = BigDecimal.ZERO,
|
slippage = BigDecimal.ZERO,
|
||||||
),
|
),
|
||||||
activeStatus = null,
|
activeStatus = ExchangeStatus.Failed,
|
||||||
statuses = persistentListOf(
|
statuses = persistentListOf(
|
||||||
ExchangeStatusState(
|
ExchangeStatusState(
|
||||||
status = ExchangeStatus.Exchanging,
|
status = ExchangeStatus.Exchanging,
|
||||||
text = stringReference("Exchanging"),
|
text = stringReference("Exchanging"),
|
||||||
isActive = true,
|
isActive = false,
|
||||||
isDone = false,
|
isDone = false,
|
||||||
),
|
),
|
||||||
|
ExchangeStatusState(
|
||||||
|
status = ExchangeStatus.Failed,
|
||||||
|
text = stringReference("Failed"),
|
||||||
|
isActive = true,
|
||||||
|
isDone = true,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
notification = ExchangeStatusNotification.LongTimeExchange(
|
notification = ExchangeStatusNotification.LongTimeExchange(
|
||||||
onGoToProviderClick = {},
|
onGoToProviderClick = {},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,409 @@
|
||||||
|
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express
|
||||||
|
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.compose.foundation.Canvas
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.ReadOnlyComposable
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.drawWithCache
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.graphics.PathEffect
|
||||||
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
|
import androidx.compose.ui.graphics.asAndroidBitmap
|
||||||
|
import androidx.compose.ui.graphics.layer.GraphicsLayer
|
||||||
|
import androidx.compose.ui.graphics.rememberGraphicsLayer
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalInspectionMode
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.vectorResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Devices
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
|
import androidx.compose.ui.unit.IntSize
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||||
|
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||||
|
import com.tangem.core.ui.components.SecondaryButtonIconStart
|
||||||
|
import com.tangem.core.ui.components.SpacerH
|
||||||
|
import com.tangem.core.ui.components.currency.icon.CurrencyIcon
|
||||||
|
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||||
|
import com.tangem.core.ui.ds.image.TangemIcon
|
||||||
|
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||||
|
import com.tangem.core.ui.extensions.resolveReference
|
||||||
|
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||||
|
import com.tangem.core.ui.res.TangemColorPalette
|
||||||
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||||
|
import com.tangem.core.ui.res.TangemThemeRedesign
|
||||||
|
import com.tangem.core.ui.utils.toPx
|
||||||
|
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
|
||||||
|
import com.tangem.features.tokendetails.impl.R
|
||||||
|
import com.tangem.utils.extensions.stripZeroPlainString
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.File
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun BoxScope.ExpressShareContent(state: ExpressTransactionStateUM) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
var graphicsLayer = rememberGraphicsLayer()
|
||||||
|
|
||||||
|
val shareText = makeExpressShareContent(state) ?: return
|
||||||
|
|
||||||
|
TangemThemeRedesign {
|
||||||
|
SecondaryButtonIconStart(
|
||||||
|
text = stringResourceSafe(R.string.common_share),
|
||||||
|
iconResId = R.drawable.ic_share_24,
|
||||||
|
onClick = {
|
||||||
|
coroutineScope.launch {
|
||||||
|
if (graphicsLayer.size.width > 0 && graphicsLayer.size.height > 0) {
|
||||||
|
val uri = graphicsLayer.saveAsShareableFile(context)
|
||||||
|
|
||||||
|
val shareIntent = if (uri != null) {
|
||||||
|
Intent().apply {
|
||||||
|
action = Intent.ACTION_SEND
|
||||||
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // Temporary external read grant
|
||||||
|
putExtra(Intent.EXTRA_TEXT, shareText)
|
||||||
|
putExtra(Intent.EXTRA_STREAM, uri)
|
||||||
|
clipData = ClipData.newRawUri(null, uri)
|
||||||
|
|
||||||
|
type = "image/png"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Intent().apply {
|
||||||
|
action = Intent.ACTION_SEND
|
||||||
|
putExtra(Intent.EXTRA_TEXT, shareText)
|
||||||
|
type = "text/plain"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.startActivity(Intent.createChooser(shareIntent, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp)
|
||||||
|
.align(Alignment.BottomCenter),
|
||||||
|
)
|
||||||
|
ExpressShareImageContent(
|
||||||
|
state = state,
|
||||||
|
onGraphicsLayer = { layer -> graphicsLayer = layer },
|
||||||
|
modifier = Modifier.size(0.dp), // size 0 so that no space is used in the UI
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ExpressShareImageContent(
|
||||||
|
state: ExpressTransactionStateUM,
|
||||||
|
onGraphicsLayer: (GraphicsLayer) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Box(modifier = modifier.drawForShare(onGraphicsLayer)) {
|
||||||
|
Box(
|
||||||
|
// override the parent size with desired size of the recording
|
||||||
|
modifier = Modifier
|
||||||
|
.wrapContentHeight(unbounded = true, align = Alignment.Top)
|
||||||
|
.wrapContentWidth(unbounded = true, align = Alignment.Start)
|
||||||
|
.requiredSize(525.dp, 580.dp),
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.matchParentSize()
|
||||||
|
.background(TangemColorPalette.Black),
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(
|
||||||
|
start = 40.dp,
|
||||||
|
end = 40.dp,
|
||||||
|
top = 40.dp,
|
||||||
|
bottom = 20.dp,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = ImageVector.vectorResource(R.drawable.img_tangem_logo_90_24),
|
||||||
|
contentDescription = null,
|
||||||
|
tint = TangemColorPalette.White,
|
||||||
|
)
|
||||||
|
SpacerH(36.dp)
|
||||||
|
|
||||||
|
ExpressShareImageAmount(
|
||||||
|
prefix = stringResourceSafe(R.string.common_send),
|
||||||
|
amountValue = state.info.fromAmountValue,
|
||||||
|
currencyIconState = state.info.fromCurrencyIcon,
|
||||||
|
currencySymbol = state.info.fromAmountSymbol,
|
||||||
|
)
|
||||||
|
ExpressShareImageAddress(
|
||||||
|
prefix = stringResourceSafe(R.string.common_from),
|
||||||
|
address = state.info.fromAddress,
|
||||||
|
)
|
||||||
|
|
||||||
|
SpacerH(24.dp)
|
||||||
|
ExpressShareImageSeparator()
|
||||||
|
SpacerH(24.dp)
|
||||||
|
|
||||||
|
ExpressShareImageAmount(
|
||||||
|
prefix = stringResourceSafe(R.string.common_receive),
|
||||||
|
amountValue = state.info.toAmountValue,
|
||||||
|
currencyIconState = state.info.toCurrencyIcon,
|
||||||
|
currencySymbol = state.info.toAmountSymbol,
|
||||||
|
)
|
||||||
|
ExpressShareImageAddress(
|
||||||
|
prefix = stringResourceSafe(R.string.common_to),
|
||||||
|
address = state.info.toAddress,
|
||||||
|
)
|
||||||
|
SpacerH(24.dp)
|
||||||
|
|
||||||
|
ExpressShareImageProvider(state)
|
||||||
|
Text(
|
||||||
|
text = stringResourceSafe(R.string.express_transaction_id, state.info.txExternalId.orEmpty()),
|
||||||
|
style = TangemTheme.typography2.bodySemibold16,
|
||||||
|
color = TangemTheme.colors3.text.staticDark.secondary,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.drawable.img_share_express_background),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier,
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ExpressShareImageAmount(
|
||||||
|
prefix: String,
|
||||||
|
amountValue: BigDecimal,
|
||||||
|
currencyIconState: CurrencyIconState,
|
||||||
|
currencySymbol: String,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = prefix,
|
||||||
|
color = TangemTheme.colors3.text.staticDark.primary,
|
||||||
|
style = TangemTheme.typography2.bodySemibold16,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = amountValue.stripZeroPlainString(),
|
||||||
|
color = TangemTheme.colors3.text.staticDark.primary,
|
||||||
|
style = TangemTheme.typography2.bodySemibold16,
|
||||||
|
)
|
||||||
|
CurrencyIcon(
|
||||||
|
state = currencyIconState,
|
||||||
|
iconSize = 20.dp,
|
||||||
|
shouldDisplayNetwork = false,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = currencySymbol,
|
||||||
|
color = TangemTheme.colors3.text.staticDark.primary,
|
||||||
|
style = TangemTheme.typography2.bodySemibold16,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ExpressShareImageAddress(prefix: String, address: String?) {
|
||||||
|
if (address != null) {
|
||||||
|
Text(
|
||||||
|
text = "$prefix:",
|
||||||
|
color = TangemTheme.colors3.text.staticDark.secondary,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = address,
|
||||||
|
color = TangemTheme.colors3.text.staticDark.secondary,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ExpressShareImageSeparator() {
|
||||||
|
val width = 1.dp
|
||||||
|
val height = 39.dp
|
||||||
|
val dashOnInterval = (width * 2).toPx()
|
||||||
|
val dashOffInterval = (width * 2).toPx()
|
||||||
|
|
||||||
|
val pathEffect = PathEffect.dashPathEffect(
|
||||||
|
intervals = floatArrayOf(dashOnInterval, dashOffInterval),
|
||||||
|
)
|
||||||
|
Canvas(modifier = Modifier.size(width = width, height = height)) {
|
||||||
|
drawLine(
|
||||||
|
color = TangemColorPalette.Dark2,
|
||||||
|
start = Offset(0f, 0f),
|
||||||
|
end = Offset(size.width, size.height),
|
||||||
|
strokeWidth = width.toPx(),
|
||||||
|
cap = StrokeCap.Round,
|
||||||
|
pathEffect = pathEffect,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ExpressShareImageProvider(state: ExpressTransactionStateUM) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResourceSafe(R.string.express_by_provider),
|
||||||
|
style = TangemTheme.typography2.bodySemibold16,
|
||||||
|
color = TangemTheme.colors3.text.staticDark.primary,
|
||||||
|
)
|
||||||
|
val providerImageUrl = when (state) {
|
||||||
|
is ExchangeUM -> state.provider.imageLarge
|
||||||
|
is ExpressTransactionStateUM.OnrampUM -> state.providerImageUrl
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
val providerName = when (state) {
|
||||||
|
is ExchangeUM -> state.provider.name
|
||||||
|
is ExpressTransactionStateUM.OnrampUM -> state.providerName
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
val providerType = when (state) {
|
||||||
|
is ExchangeUM -> state.provider.type.providerName
|
||||||
|
is ExpressTransactionStateUM.OnrampUM -> state.providerType
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
if (providerImageUrl != null) {
|
||||||
|
TangemIcon(
|
||||||
|
tangemIconUM = TangemIconUM.Url(providerImageUrl, fallbackRes = R.drawable.ic_empty_64),
|
||||||
|
modifier = Modifier.size(20.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = providerName.orEmpty(),
|
||||||
|
style = TangemTheme.typography2.bodySemibold16,
|
||||||
|
color = TangemTheme.colors3.text.staticDark.primary,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = providerType.orEmpty(),
|
||||||
|
style = TangemTheme.typography2.bodySemibold16,
|
||||||
|
color = TangemTheme.colors3.text.staticDark.secondary,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Modifier.drawForShare(onGraphicsLayer: (GraphicsLayer) -> Unit): Modifier {
|
||||||
|
val isInspectionMode = LocalInspectionMode.current
|
||||||
|
|
||||||
|
return drawWithCache {
|
||||||
|
// draw to graphics layer
|
||||||
|
onGraphicsLayer(
|
||||||
|
obtainGraphicsLayer().apply {
|
||||||
|
record(
|
||||||
|
size = IntSize(
|
||||||
|
width = 525.dp.toPx().toInt(),
|
||||||
|
height = 580.dp.toPx().toInt(),
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
drawContent()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isInspectionMode) {
|
||||||
|
// draw only for preview mode
|
||||||
|
onDrawWithContent { drawContent() }
|
||||||
|
} else {
|
||||||
|
// leave blank to skip drawing on the screen
|
||||||
|
onDrawWithContent { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("MagicNumber")
|
||||||
|
private suspend fun GraphicsLayer.saveAsShareableFile(context: Context): Uri? {
|
||||||
|
// convert to bitmap
|
||||||
|
val bitmap = this.toImageBitmap().asAndroidBitmap()
|
||||||
|
|
||||||
|
// create file
|
||||||
|
val cachePath = File(context.cacheDir, "images")
|
||||||
|
cachePath.mkdir()
|
||||||
|
|
||||||
|
val file = File(cachePath, "shared_image.png")
|
||||||
|
|
||||||
|
// write bitmap to file as PNG
|
||||||
|
file.outputStream().use { out ->
|
||||||
|
bitmap.compress(/* format = */ Bitmap.CompressFormat.PNG, /* quality = */ 100, /* stream = */ out)
|
||||||
|
out.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate secure Content URI using the registered authority
|
||||||
|
return FileProvider.getUriForFile(
|
||||||
|
/* context = */ context,
|
||||||
|
/* authority = */ "${context.packageName}.provider",
|
||||||
|
/* file = */ file,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReadOnlyComposable
|
||||||
|
@Composable
|
||||||
|
internal fun makeExpressShareContent(state: ExpressTransactionStateUM): String? {
|
||||||
|
val txExternalId = state.info.txExternalId
|
||||||
|
val txId = if (txExternalId != null) {
|
||||||
|
stringResourceSafe(R.string.express_transaction_id, txExternalId)
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
|
||||||
|
val (providerName, providerType) = when (state) {
|
||||||
|
is ExchangeUM -> state.provider.name to state.provider.type
|
||||||
|
is ExpressTransactionStateUM.OnrampUM -> state.providerName to state.providerType
|
||||||
|
else -> return null
|
||||||
|
}
|
||||||
|
val providerInfo = "${stringResourceSafe(R.string.express_by_provider)} $providerName $providerType"
|
||||||
|
val fromAddress = if (state.info.fromAddress != null) {
|
||||||
|
"${stringResourceSafe(R.string.common_from)}: ${state.info.fromAddress}"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
val text = """
|
||||||
|
${stringResourceSafe(R.string.common_tangem)}
|
||||||
|
|
||||||
|
${stringResourceSafe(R.string.common_send)} ${state.info.fromAmount.resolveReference()}
|
||||||
|
$fromAddress
|
||||||
|
|
||||||
|
${stringResourceSafe(R.string.common_receive)} ${state.info.toAmount.resolveReference()}
|
||||||
|
${stringResourceSafe(R.string.common_to)}: ${state.info.toAddress}
|
||||||
|
|
||||||
|
$providerInfo
|
||||||
|
$txId
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
// region Preview
|
||||||
|
@Composable
|
||||||
|
@Preview(showBackground = true, widthDp = 360, device = Devices.PIXEL_7_PRO)
|
||||||
|
@Preview(showBackground = true, widthDp = 360, device = Devices.PIXEL_7_PRO, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
private fun ExpressShareImageContent_Preview(
|
||||||
|
@PreviewParameter(ExpressStatusBottomSheetStateProvider::class) param: ExpressStatusBottomSheetConfig,
|
||||||
|
) {
|
||||||
|
TangemThemePreviewRedesign {
|
||||||
|
ExpressShareImageContent(param.value, {}, modifier = Modifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
@ -10,10 +10,12 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import com.tangem.common.ui.expressStatus.ExpressEstimate
|
import com.tangem.common.ui.expressStatus.ExpressEstimate
|
||||||
import com.tangem.common.ui.expressStatus.ExpressHideButton
|
import com.tangem.common.ui.expressStatus.ExpressHideButton
|
||||||
import com.tangem.common.ui.expressStatus.ExpressProvider
|
import com.tangem.common.ui.expressStatus.ExpressProvider
|
||||||
import com.tangem.core.ui.R
|
import com.tangem.core.ui.R
|
||||||
|
import com.tangem.core.ui.components.SpacerH
|
||||||
import com.tangem.core.ui.components.SpacerH10
|
import com.tangem.core.ui.components.SpacerH10
|
||||||
import com.tangem.core.ui.components.SpacerH12
|
import com.tangem.core.ui.components.SpacerH12
|
||||||
import com.tangem.core.ui.components.SpacerH16
|
import com.tangem.core.ui.components.SpacerH16
|
||||||
|
|
@ -28,7 +30,11 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.component
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
|
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ExchangeStatusBottomSheetContent(state: ExchangeUM, extraContent: (@Composable () -> Unit)? = null) {
|
internal fun ExchangeStatusBottomSheetContent(
|
||||||
|
state: ExchangeUM,
|
||||||
|
isExpressShareButtonEnabled: Boolean,
|
||||||
|
extraContent: (@Composable () -> Unit)? = null,
|
||||||
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||||
|
|
@ -87,8 +93,12 @@ internal fun ExchangeStatusBottomSheetContent(state: ExchangeUM, extraContent: (
|
||||||
isAutoDisposable = state.activeStatus?.isAutoDisposable == true,
|
isAutoDisposable = state.activeStatus?.isAutoDisposable == true,
|
||||||
onClick = state.info.onDisposeExpressStatus,
|
onClick = state.info.onDisposeExpressStatus,
|
||||||
)
|
)
|
||||||
|
if (isExpressShareButtonEnabled) {
|
||||||
|
SpacerH(80.dp)
|
||||||
|
} else {
|
||||||
SpacerH24()
|
SpacerH24()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 196 KiB |
|
|
@ -63,6 +63,7 @@ internal class SingleWalletOnrampTransactionConverter(
|
||||||
timestampAgoFormatted = mapFormattedDate(value.timestamp),
|
timestampAgoFormatted = mapFormattedDate(value.timestamp),
|
||||||
activeStatus = value.status.toActiveStatusText(currency.name),
|
activeStatus = value.status.toActiveStatusText(currency.name),
|
||||||
toAmount = stringReference(value.toAmount.format { crypto(currency) }),
|
toAmount = stringReference(value.toAmount.format { crypto(currency) }),
|
||||||
|
toAmountValue = value.toAmount,
|
||||||
toFiatAmount = stringReference(
|
toFiatAmount = stringReference(
|
||||||
status.fiatRate?.multiply(value.toAmount).format {
|
status.fiatRate?.multiply(value.toAmount).format {
|
||||||
fiat(
|
fiat(
|
||||||
|
|
@ -73,6 +74,7 @@ internal class SingleWalletOnrampTransactionConverter(
|
||||||
),
|
),
|
||||||
toAmountSymbol = currency.symbol,
|
toAmountSymbol = currency.symbol,
|
||||||
toCurrencyIcon = iconStateConverter.convert(currency),
|
toCurrencyIcon = iconStateConverter.convert(currency),
|
||||||
|
toAddress = status.networkAddress?.defaultAddress?.value.orEmpty(),
|
||||||
fromAmount = stringReference(
|
fromAmount = stringReference(
|
||||||
value.fromAmount.format {
|
value.fromAmount.format {
|
||||||
fiat(
|
fiat(
|
||||||
|
|
@ -81,12 +83,14 @@ internal class SingleWalletOnrampTransactionConverter(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
fromAmountValue = value.fromAmount,
|
||||||
fromFiatAmount = null,
|
fromFiatAmount = null,
|
||||||
fromAmountSymbol = value.fromCurrency.code,
|
fromAmountSymbol = value.fromCurrency.code,
|
||||||
fromCurrencyIcon = CurrencyIconState.FiatIcon(
|
fromCurrencyIcon = CurrencyIconState.FiatIcon(
|
||||||
url = value.fromCurrency.image,
|
url = value.fromCurrency.image,
|
||||||
fallbackResId = R.drawable.ic_currency_24,
|
fallbackResId = R.drawable.ic_currency_24,
|
||||||
),
|
),
|
||||||
|
fromAddress = null,
|
||||||
iconState = value.status.toIconState(),
|
iconState = value.status.toIconState(),
|
||||||
onGoToProviderClick = { url ->
|
onGoToProviderClick = { url ->
|
||||||
analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider())
|
analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue