Updated on 2026-08-14
This commit is contained in:
parent
b81ea5cf6e
commit
8beefdf5e9
36 changed files with 859 additions and 141 deletions
|
|
@ -604,7 +604,7 @@ internal class ChildFactory @Inject constructor(
|
|||
is AppRoute.TangemPayDetails -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = TangemPayDetailsComponent.Params(config = route.config),
|
||||
params = TangemPayDetailsComponent.Params(userWalletId = route.userWalletId, config = route.config),
|
||||
componentFactory = tangemPayDetailsComponentFactory,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -383,8 +383,9 @@ sealed class AppRoute(val path: String) : Route {
|
|||
|
||||
@Serializable
|
||||
data class TangemPayDetails(
|
||||
val userWalletId: UserWalletId,
|
||||
val config: TangemPayDetailsConfig,
|
||||
) : AppRoute(path = "/tangem_pay_details")
|
||||
) : AppRoute(path = "/tangem_pay_details/${userWalletId.stringValue}")
|
||||
|
||||
@Serializable
|
||||
data class TangemPayOnboarding(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
package com.tangem.core.ui.extensions
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
/**
|
||||
* Utility class for keeping themed color reference from app theme.
|
||||
*
|
||||
* It necessary to use [Immutable] annotation for runtime stability.
|
||||
*
|
||||
* @property value color provider from theme
|
||||
*/
|
||||
@Immutable
|
||||
data class ColorReference(val value: @Composable () -> Color)
|
||||
|
||||
/**
|
||||
* Creates a [ColorReference] using a themed color from the app theme with a lambda.
|
||||
*
|
||||
* @param value The color provider from theme.
|
||||
* @return A [ColorReference] representing the themed color.
|
||||
*/
|
||||
fun themedColor(value: @Composable () -> Color): ColorReference {
|
||||
return ColorReference(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves [ColorReference] to [Color]
|
||||
*/
|
||||
@Composable
|
||||
fun ColorReference.resolveReference(): Color {
|
||||
return value()
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
package com.tangem.data.pay.repository
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.visa.utils.TangemPayTxHistoryItemConverter
|
||||
import com.tangem.datasource.api.pay.TangemPayApi
|
||||
import com.tangem.datasource.di.NetworkMoshi
|
||||
import com.tangem.datasource.local.visa.TangemPayTxHistoryItemsStore
|
||||
import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchFlow
|
||||
import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchingContext
|
||||
|
|
@ -26,8 +28,11 @@ internal class DefaultTangemPayTxHistoryRepository @Inject constructor(
|
|||
private val cacheRegistry: CacheRegistry,
|
||||
private val txHistoryItemsStore: TangemPayTxHistoryItemsStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
@NetworkMoshi private val moshi: Moshi,
|
||||
) : TangemPayTxHistoryRepository {
|
||||
|
||||
private val txHistoryItemConverter by lazy { TangemPayTxHistoryItemConverter(moshi) }
|
||||
|
||||
override fun getTxHistoryBatchFlow(
|
||||
batchSize: Int,
|
||||
context: TangemPayTxHistoryListBatchingContext,
|
||||
|
|
@ -84,7 +89,7 @@ internal class DefaultTangemPayTxHistoryRepository @Inject constructor(
|
|||
val result = requestPerformer.request { authHeader ->
|
||||
visaApi.getTangemPayTxHistory(authHeader = authHeader, limit = pageSize, cursor = cursor)
|
||||
}.result
|
||||
val items = TangemPayTxHistoryItemConverter.convertList(result.transactions).filterNotNull()
|
||||
val items = txHistoryItemConverter.convertList(result.transactions).filterNotNull()
|
||||
txHistoryItemsStore.store(key = customerWalletAddress, cursor = cursor ?: INITIAL_CURSOR, value = items)
|
||||
}.onLeft { error(it.toString()) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,19 @@
|
|||
package com.tangem.data.visa.utils
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.datasource.api.pay.models.response.TangemPayTxHistoryResponse
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import com.tangem.utils.converter.Converter
|
||||
import timber.log.Timber
|
||||
import java.util.Currency
|
||||
|
||||
internal object TangemPayTxHistoryItemConverter :
|
||||
internal class TangemPayTxHistoryItemConverter(moshi: Moshi) :
|
||||
Converter<TangemPayTxHistoryResponse.Transaction, TangemPayTxHistoryItem?> {
|
||||
|
||||
private val spendAdapter = moshi.adapter(TangemPayTxHistoryResponse.Spend::class.java)
|
||||
private val paymentAdapter = moshi.adapter(TangemPayTxHistoryResponse.Payment::class.java)
|
||||
private val feeAdapter = moshi.adapter(TangemPayTxHistoryResponse.Fee::class.java)
|
||||
|
||||
override fun convert(value: TangemPayTxHistoryResponse.Transaction): TangemPayTxHistoryItem? {
|
||||
return value.spend?.let { convertSpend(id = value.id, spend = it) }
|
||||
?: value.payment?.let { convertPayment(id = value.id, payment = it) }
|
||||
|
|
@ -22,6 +27,7 @@ internal object TangemPayTxHistoryItemConverter :
|
|||
private fun convertSpend(id: String, spend: TangemPayTxHistoryResponse.Spend): TangemPayTxHistoryItem.Spend {
|
||||
return TangemPayTxHistoryItem.Spend(
|
||||
id = id,
|
||||
jsonRepresentation = spendAdapter.toJson(spend),
|
||||
// If postedAt is null, it means transaction wasn't posted and was likely declined. Use authorizedAt
|
||||
date = spend.postedAt ?: spend.authorizedAt,
|
||||
amount = spend.amount,
|
||||
|
|
@ -41,15 +47,18 @@ internal object TangemPayTxHistoryItemConverter :
|
|||
): TangemPayTxHistoryItem.Payment {
|
||||
return TangemPayTxHistoryItem.Payment(
|
||||
id = id,
|
||||
jsonRepresentation = paymentAdapter.toJson(payment),
|
||||
date = payment.postedAt,
|
||||
currency = Currency.getInstance(payment.currency),
|
||||
amount = payment.amount,
|
||||
transactionHash = payment.transactionHash,
|
||||
)
|
||||
}
|
||||
|
||||
private fun convertFee(id: String, fee: TangemPayTxHistoryResponse.Fee): TangemPayTxHistoryItem.Fee {
|
||||
return TangemPayTxHistoryItem.Fee(
|
||||
id = id,
|
||||
jsonRepresentation = feeAdapter.toJson(fee),
|
||||
date = fee.postedAt,
|
||||
currency = Currency.getInstance(fee.currency),
|
||||
amount = fee.amount,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.domain.feedback.models
|
||||
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import com.tangem.domain.visa.model.VisaTxDetails
|
||||
|
||||
/**
|
||||
|
|
@ -64,5 +65,10 @@ sealed interface FeedbackEmailType {
|
|||
val visaTxDetails: VisaTxDetails,
|
||||
override val walletMetaInfo: WalletMetaInfo,
|
||||
) : Visa()
|
||||
|
||||
data class DisputeV2(
|
||||
val item: TangemPayTxHistoryItem,
|
||||
override val walletMetaInfo: WalletMetaInfo,
|
||||
) : Visa()
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.domain.feedback
|
|||
|
||||
import com.tangem.domain.feedback.models.*
|
||||
import com.tangem.domain.feedback.utils.breakLine
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import com.tangem.domain.visa.model.VisaTxDetails
|
||||
import com.tangem.domain.feedback.models.BlockchainInfo.Addresses as BlockchainAddresses
|
||||
|
||||
|
|
@ -9,6 +10,10 @@ internal class FeedbackDataBuilder {
|
|||
|
||||
private val builder = StringBuilder()
|
||||
|
||||
fun addTangemPayTxInfo(item: TangemPayTxHistoryItem) {
|
||||
builder.append(item.jsonRepresentation)
|
||||
}
|
||||
|
||||
fun addVisaTxInfo(txDetails: VisaTxDetails) {
|
||||
builder.appendKeyValue("Type", txDetails.type)
|
||||
builder.appendKeyValue("Status", txDetails.status)
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ class SendFeedbackEmailUseCase(
|
|||
is FeedbackEmailType.PreActivatedWallet,
|
||||
is FeedbackEmailType.CardAttestationFailed,
|
||||
is FeedbackEmailType.Visa.Dispute,
|
||||
is FeedbackEmailType.Visa.DisputeV2,
|
||||
-> this
|
||||
is FeedbackEmailType.DirectUserRequest,
|
||||
is FeedbackEmailType.RateCanBeBetter,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
package com.tangem.domain.feedback.utils
|
||||
|
||||
import com.tangem.domain.feedback.FeedbackDataBuilder
|
||||
import com.tangem.domain.feedback.models.WalletMetaInfo
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
import com.tangem.domain.feedback.models.WalletMetaInfo
|
||||
import com.tangem.domain.feedback.repository.FeedbackRepository
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import com.tangem.domain.visa.model.VisaTxDetails
|
||||
|
||||
/**
|
||||
|
|
@ -33,11 +34,21 @@ internal class EmailMessageBodyResolver(
|
|||
is FeedbackEmailType.Visa.Activation -> addUserRequestBody(type.walletMetaInfo)
|
||||
is FeedbackEmailType.Visa.DirectUserRequest -> addUserRequestBody(type.walletMetaInfo)
|
||||
is FeedbackEmailType.Visa.Dispute -> addVisaRequestBody(type.walletMetaInfo, type.visaTxDetails)
|
||||
is FeedbackEmailType.Visa.DisputeV2 -> addTangemPayRequestBody(type.walletMetaInfo, type.item)
|
||||
}
|
||||
|
||||
return build()
|
||||
}
|
||||
|
||||
private suspend fun FeedbackDataBuilder.addTangemPayRequestBody(
|
||||
walletMetaInfo: WalletMetaInfo,
|
||||
item: TangemPayTxHistoryItem,
|
||||
) {
|
||||
addUserRequestBody(walletMetaInfo)
|
||||
addDelimiter()
|
||||
addTangemPayTxInfo(item)
|
||||
}
|
||||
|
||||
private suspend fun FeedbackDataBuilder.addVisaRequestBody(
|
||||
walletMetaInfo: WalletMetaInfo,
|
||||
visaTxDetails: VisaTxDetails,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ internal class EmailMessageTitleResolver(private val resources: Resources) {
|
|||
is FeedbackEmailType.Visa.Activation,
|
||||
is FeedbackEmailType.Visa.DirectUserRequest,
|
||||
is FeedbackEmailType.Visa.Dispute,
|
||||
is FeedbackEmailType.Visa.DisputeV2,
|
||||
-> R.string.feedback_preface_support
|
||||
is FeedbackEmailType.RateCanBeBetter -> R.string.feedback_preface_rate_negative
|
||||
is FeedbackEmailType.ScanningProblem -> R.string.feedback_preface_scan_failed
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@ internal class EmailSubjectResolver(private val resources: Resources) {
|
|||
FeedbackEmailType.CardAttestationFailed -> "Card attestation failed"
|
||||
is FeedbackEmailType.Visa.Activation -> "[Visa] [Activation] {auto-filled subject}"
|
||||
is FeedbackEmailType.Visa.DirectUserRequest -> "[Visa] {auto-filled subject}"
|
||||
is FeedbackEmailType.Visa.Dispute -> "[Visa] [DISPUTE] {auto-filled subject}"
|
||||
is FeedbackEmailType.Visa.Dispute,
|
||||
is FeedbackEmailType.Visa.DisputeV2,
|
||||
-> "[Visa] [DISPUTE] {auto-filled subject}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ dependencies {
|
|||
implementation(deps.moshi.kotlin)
|
||||
implementation(deps.moshi.adapters)
|
||||
implementation(deps.kotlin.datetime)
|
||||
implementation(deps.jodatime)
|
||||
implementation(deps.kotlin.serialization)
|
||||
ksp(deps.moshi.kotlin.codegen)
|
||||
implementation(deps.arrow.core)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.domain.models.serialization
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import java.util.Currency
|
||||
|
||||
typealias SerializedCurrency = @Serializable(with = CurrencySerializer::class) Currency
|
||||
|
||||
object CurrencySerializer : KSerializer<Currency> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("java.util.Currency", PrimitiveKind.STRING)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Currency) {
|
||||
encoder.encodeString(value.currencyCode)
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): Currency {
|
||||
return Currency.getInstance(decoder.decodeString())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.domain.models.serialization
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import org.joda.time.DateTime
|
||||
|
||||
typealias SerializedDateTime = @Serializable(with = JodaDateTimeSerializer::class) DateTime
|
||||
|
||||
object JodaDateTimeSerializer : KSerializer<DateTime> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("DateTime", PrimitiveKind.LONG)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: DateTime) {
|
||||
encoder.encodeLong(value.millis)
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): DateTime {
|
||||
return DateTime(decoder.decodeLong())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,25 @@
|
|||
package com.tangem.domain.visa.model
|
||||
|
||||
import org.joda.time.DateTime
|
||||
import java.math.BigDecimal
|
||||
import java.util.Currency
|
||||
import com.tangem.domain.models.serialization.SerializedBigDecimal
|
||||
import com.tangem.domain.models.serialization.SerializedCurrency
|
||||
import com.tangem.domain.models.serialization.SerializedDateTime
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
sealed class TangemPayTxHistoryItem {
|
||||
abstract val id: String
|
||||
abstract val date: DateTime
|
||||
abstract val amount: BigDecimal
|
||||
abstract val currency: Currency
|
||||
abstract val date: SerializedDateTime
|
||||
abstract val amount: SerializedBigDecimal
|
||||
abstract val currency: SerializedCurrency
|
||||
abstract val jsonRepresentation: String
|
||||
|
||||
@Serializable
|
||||
data class Spend(
|
||||
override val id: String,
|
||||
override val date: DateTime,
|
||||
override val amount: BigDecimal,
|
||||
override val currency: Currency,
|
||||
override val jsonRepresentation: String,
|
||||
override val date: SerializedDateTime,
|
||||
override val amount: SerializedBigDecimal,
|
||||
override val currency: SerializedCurrency,
|
||||
val enrichedMerchantName: String?,
|
||||
val merchantName: String,
|
||||
val enrichedMerchantCategory: String?,
|
||||
|
|
@ -23,18 +28,23 @@ sealed class TangemPayTxHistoryItem {
|
|||
val enrichedMerchantIconUrl: String?,
|
||||
) : TangemPayTxHistoryItem()
|
||||
|
||||
@Serializable
|
||||
data class Payment(
|
||||
override val id: String,
|
||||
override val date: DateTime,
|
||||
override val amount: BigDecimal,
|
||||
override val currency: Currency,
|
||||
override val jsonRepresentation: String,
|
||||
override val date: SerializedDateTime,
|
||||
override val amount: SerializedBigDecimal,
|
||||
override val currency: SerializedCurrency,
|
||||
val transactionHash: String?,
|
||||
) : TangemPayTxHistoryItem()
|
||||
|
||||
@Serializable
|
||||
data class Fee(
|
||||
override val id: String,
|
||||
override val date: DateTime,
|
||||
override val amount: BigDecimal,
|
||||
override val currency: Currency,
|
||||
override val jsonRepresentation: String,
|
||||
override val date: SerializedDateTime,
|
||||
override val amount: SerializedBigDecimal,
|
||||
override val currency: SerializedCurrency,
|
||||
) : TangemPayTxHistoryItem()
|
||||
|
||||
enum class Status {
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ package com.tangem.features.tangempay.components
|
|||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.TangemPayDetailsConfig
|
||||
|
||||
interface TangemPayDetailsComponent : ComposableContentComponent {
|
||||
data class Params(val config: TangemPayDetailsConfig)
|
||||
data class Params(val userWalletId: UserWalletId, val config: TangemPayDetailsConfig)
|
||||
interface Factory : ComponentFactory<Params, TangemPayDetailsComponent>
|
||||
}
|
||||
|
|
@ -15,8 +15,9 @@ dependencies {
|
|||
/** Core */
|
||||
implementation(projects.core.configToggles)
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.ui)
|
||||
implementation(projects.core.error)
|
||||
implementation(projects.core.navigation)
|
||||
implementation(projects.core.ui)
|
||||
|
||||
/** Features api */
|
||||
implementation(projects.features.tangempay.details.api)
|
||||
|
|
@ -26,9 +27,12 @@ dependencies {
|
|||
/** Domain */
|
||||
implementation(projects.domain.balanceHiding)
|
||||
implementation(projects.domain.balanceHiding.models)
|
||||
implementation(projects.domain.feedback)
|
||||
implementation(projects.domain.feedback.models)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.visa)
|
||||
implementation(projects.domain.visa.models)
|
||||
implementation(projects.domain.wallets)
|
||||
|
||||
/** Compose */
|
||||
implementation(deps.compose.coil)
|
||||
|
|
|
|||
|
|
@ -15,8 +15,9 @@ import com.tangem.core.decompose.model.getOrCreateModel
|
|||
import com.tangem.core.ui.components.NavigationBar3ButtonsScrim
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.features.tangempay.components.txHistory.DefaultTangemPayTxHistoryComponent
|
||||
import com.tangem.features.tangempay.components.txHistory.TangemPayTxHistoryDetailsComponent
|
||||
import com.tangem.features.tangempay.model.TangemPayDetailsModel
|
||||
import com.tangem.features.tangempay.model.TangemPayDetailsNavigation
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsNavigation
|
||||
import com.tangem.features.tangempay.ui.TangemPayDetailsScreen
|
||||
import com.tangem.features.tokenreceive.TokenReceiveComponent
|
||||
import dagger.assisted.Assisted
|
||||
|
|
@ -39,7 +40,10 @@ internal class DefaultTangemPayDetailsComponent @AssistedInject constructor(
|
|||
)
|
||||
private val txHistoryComponent = DefaultTangemPayTxHistoryComponent(
|
||||
appComponentContext = child("txHistoryComponent"),
|
||||
params = DefaultTangemPayTxHistoryComponent.Params(customerWalletAddress = params.config.customerWalletAddress),
|
||||
params = DefaultTangemPayTxHistoryComponent.Params(
|
||||
customerWalletAddress = params.config.customerWalletAddress,
|
||||
uiActions = model,
|
||||
),
|
||||
)
|
||||
|
||||
@Composable
|
||||
|
|
@ -59,19 +63,25 @@ internal class DefaultTangemPayDetailsComponent @AssistedInject constructor(
|
|||
private fun bottomSheetChild(
|
||||
navigation: TangemPayDetailsNavigation,
|
||||
componentContext: ComponentContext,
|
||||
): ComposableBottomSheetComponent = when (navigation) {
|
||||
is TangemPayDetailsNavigation.Error -> TangemPayErrorBottomSheetComponent(
|
||||
appComponentContext = appComponentContext,
|
||||
messageUM = navigation.messageUM,
|
||||
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||
)
|
||||
is TangemPayDetailsNavigation.Receive -> tokenReceiveComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = TokenReceiveComponent.Params(
|
||||
config = navigation.config,
|
||||
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||
),
|
||||
)
|
||||
): ComposableBottomSheetComponent {
|
||||
val context = childByContext(componentContext)
|
||||
return when (navigation) {
|
||||
is TangemPayDetailsNavigation.Receive -> tokenReceiveComponentFactory.create(
|
||||
context = context,
|
||||
params = TokenReceiveComponent.Params(
|
||||
config = navigation.config,
|
||||
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||
),
|
||||
)
|
||||
is TangemPayDetailsNavigation.TransactionDetails -> TangemPayTxHistoryDetailsComponent(
|
||||
appComponentContext = context,
|
||||
params = TangemPayTxHistoryDetailsComponent.Params(
|
||||
transaction = navigation.transaction,
|
||||
userWalletId = params.userWalletId,
|
||||
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
package com.tangem.features.tangempay.components
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetV2
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
|
||||
internal class TangemPayErrorBottomSheetComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
private val messageUM: MessageBottomSheetUMV2,
|
||||
private val onDismiss: () -> Unit,
|
||||
) : AppComponentContext by appComponentContext, ComposableBottomSheetComponent {
|
||||
|
||||
override fun dismiss() {
|
||||
onDismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
MessageBottomSheetV2(state = messageUM, onDismissRequest = ::dismiss)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import com.tangem.core.decompose.model.getOrCreateModel
|
|||
import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM
|
||||
import com.tangem.features.tangempay.model.TangemPayTxHistoryModel
|
||||
import com.tangem.features.tangempay.ui.tangemPayTxHistoryItems
|
||||
import com.tangem.features.tangempay.utils.TangemPayTxHistoryUiActions
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
internal class DefaultTangemPayTxHistoryComponent(
|
||||
|
|
@ -21,5 +22,5 @@ internal class DefaultTangemPayTxHistoryComponent(
|
|||
tangemPayTxHistoryItems(listState, state)
|
||||
}
|
||||
|
||||
data class Params(val customerWalletAddress: String)
|
||||
data class Params(val customerWalletAddress: String, val uiActions: TangemPayTxHistoryUiActions)
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.tangem.features.tangempay.components.txHistory
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import com.tangem.features.tangempay.model.TangemPayTxHistoryDetailsModel
|
||||
import com.tangem.features.tangempay.ui.TangemPayTxHistoryDetailsContent
|
||||
|
||||
internal class TangemPayTxHistoryDetailsComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
params: Params,
|
||||
) : ComposableBottomSheetComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: TangemPayTxHistoryDetailsModel = getOrCreateModel(params = params)
|
||||
|
||||
override fun dismiss() {
|
||||
model.dismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
TangemPayTxHistoryDetailsContent(state = model.uiState)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val transaction: TangemPayTxHistoryItem,
|
||||
val userWalletId: UserWalletId,
|
||||
val onDismiss: () -> Unit,
|
||||
)
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.tangempay.di
|
|||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.tangempay.model.TangemPayDetailsModel
|
||||
import com.tangem.features.tangempay.model.TangemPayTxHistoryDetailsModel
|
||||
import com.tangem.features.tangempay.model.TangemPayTxHistoryModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
|
|
@ -23,4 +24,9 @@ internal interface TangemPayModelModule {
|
|||
@IntoMap
|
||||
@ClassKey(TangemPayTxHistoryModel::class)
|
||||
fun bindTangemPayTxHistoryModel(model: TangemPayTxHistoryModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(TangemPayTxHistoryDetailsModel::class)
|
||||
fun bindTangemPayTxHistoryDetailsModel(model: TangemPayTxHistoryDetailsModel): Model
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.features.tangempay.entity
|
||||
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
internal sealed class TangemPayDetailsNavigation {
|
||||
|
||||
@Serializable
|
||||
data class Receive(val config: TokenReceiveConfig) : TangemPayDetailsNavigation()
|
||||
|
||||
@Serializable
|
||||
data class TransactionDetails(val transaction: TangemPayTxHistoryItem) : TangemPayDetailsNavigation()
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.tangem.features.tangempay.entity
|
||||
|
||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.extensions.ColorReference
|
||||
import com.tangem.core.ui.extensions.ImageReference
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
internal data class TangemPayTxHistoryDetailsUM(
|
||||
val title: TextReference,
|
||||
val iconState: ImageReference,
|
||||
val transactionTitle: TextReference,
|
||||
val transactionSubtitle: TextReference,
|
||||
val transactionAmount: String,
|
||||
val transactionAmountColor: ColorReference,
|
||||
val labelState: LabelUM?,
|
||||
val notification: NotificationConfig?,
|
||||
val buttonState: ButtonState,
|
||||
val dismiss: () -> Unit,
|
||||
) {
|
||||
|
||||
data class ButtonState(val text: TextReference, val onClick: () -> Unit)
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ import androidx.compose.runtime.Stable
|
|||
import androidx.compose.ui.graphics.toArgb
|
||||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
|
|
@ -19,13 +18,16 @@ import com.tangem.domain.models.TokenReceiveConfig
|
|||
import com.tangem.domain.models.TokenReceiveType
|
||||
import com.tangem.domain.pay.DataForReceiveFactory
|
||||
import com.tangem.domain.pay.repository.CardDetailsRepository
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import com.tangem.features.tangempay.components.TangemPayDetailsComponent
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsErrorType
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsNavigation
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsStateFactory
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
|
||||
import com.tangem.features.tangempay.model.transformers.*
|
||||
import com.tangem.features.tangempay.utils.TangemPayErrorMessageFactory
|
||||
import com.tangem.features.tangempay.utils.TangemPayTxHistoryUiActions
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
|
|
@ -53,7 +55,7 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
private val dataForReceiveFactory: DataForReceiveFactory,
|
||||
private val clipboardManager: ClipboardManager,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
) : Model() {
|
||||
) : Model(), TangemPayTxHistoryUiActions {
|
||||
|
||||
private val params: TangemPayDetailsComponent.Params = paramsContainer.require()
|
||||
|
||||
|
|
@ -102,11 +104,8 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
bottomSheetNavigation.activate(TangemPayDetailsNavigation.Receive(config))
|
||||
}
|
||||
.onLeft {
|
||||
val messageUM = TangemPayErrorMessageFactory.createError(
|
||||
type = TangemPayDetailsErrorType.Receive,
|
||||
onDismiss = bottomSheetNavigation::dismiss,
|
||||
)
|
||||
bottomSheetNavigation.activate(TangemPayDetailsNavigation.Error(messageUM))
|
||||
val messageUM = TangemPayErrorMessageFactory.createErrorMessage(TangemPayDetailsErrorType.Receive)
|
||||
uiMessageSender.send(message = messageUM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -163,4 +162,8 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
SnackbarMessage(TextReference.Res(R.string.tangempay_card_details_error_text)),
|
||||
)
|
||||
}
|
||||
|
||||
override fun onTransactionClick(item: TangemPayTxHistoryItem) {
|
||||
bottomSheetNavigation.activate(TangemPayDetailsNavigation.TransactionDetails(item))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package com.tangem.features.tangempay.model
|
||||
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
internal sealed class TangemPayDetailsNavigation {
|
||||
|
||||
data class Receive(
|
||||
val config: TokenReceiveConfig,
|
||||
) : TangemPayDetailsNavigation()
|
||||
|
||||
data class Error(
|
||||
val messageUM: MessageBottomSheetUMV2,
|
||||
) : TangemPayDetailsNavigation()
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package com.tangem.features.tangempay.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
|
||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
import com.tangem.domain.models.wallet.requireColdWallet
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.features.tangempay.components.txHistory.TangemPayTxHistoryDetailsComponent
|
||||
import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUM
|
||||
import com.tangem.features.tangempay.model.transformers.TangemPayTxHistoryDetailsConverter
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Stable
|
||||
@ModelScoped
|
||||
internal class TangemPayTxHistoryDetailsModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val getUserWalletsUseCase: GetWalletsUseCase,
|
||||
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val urlOpener: UrlOpener,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<TangemPayTxHistoryDetailsComponent.Params>()
|
||||
val uiState: TangemPayTxHistoryDetailsUM = TangemPayTxHistoryDetailsConverter.convert(
|
||||
TangemPayTxHistoryDetailsConverter.Input(
|
||||
item = params.transaction,
|
||||
onExplorerClick = ::openExplorer,
|
||||
onDisputeClick = ::dispute,
|
||||
onDismiss = ::dismiss,
|
||||
),
|
||||
)
|
||||
|
||||
fun dismiss() {
|
||||
params.onDismiss()
|
||||
}
|
||||
|
||||
fun openExplorer(txHash: String?) {
|
||||
txHash?.let(urlOpener::openUrlExternalBrowser)
|
||||
}
|
||||
|
||||
fun dispute() {
|
||||
modelScope.launch {
|
||||
val userWalletId = params.userWalletId
|
||||
val userWallet = getUserWalletsUseCase.invokeSync()
|
||||
.firstOrNull { it.walletId == userWalletId } ?: return@launch
|
||||
val walletMetaInfo = getWalletMetaInfoUseCase.invoke(
|
||||
userWallet.requireColdWallet().scanResponse,
|
||||
).getOrNull() ?: return@launch
|
||||
|
||||
sendFeedbackEmailUseCase.invoke(
|
||||
FeedbackEmailType.Visa.DisputeV2(
|
||||
item = params.transaction,
|
||||
walletMetaInfo = walletMetaInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,17 +6,14 @@ import com.tangem.core.decompose.model.Model
|
|||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import com.tangem.features.tangempay.components.txHistory.DefaultTangemPayTxHistoryComponent
|
||||
import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM
|
||||
import com.tangem.features.tangempay.utils.TangemPayTxHistoryListManager
|
||||
import com.tangem.features.tangempay.utils.TangemPayTxHistoryUiActions
|
||||
import com.tangem.pagination.PaginationStatus
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@Stable
|
||||
|
|
@ -26,14 +23,14 @@ internal class TangemPayTxHistoryModel @Inject constructor(
|
|||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
tangemPayTxHistoryRepository: TangemPayTxHistoryRepository,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model(), TangemPayTxHistoryUiActions {
|
||||
) : Model() {
|
||||
|
||||
private val params: DefaultTangemPayTxHistoryComponent.Params = paramsContainer.require()
|
||||
private val listManager = TangemPayTxHistoryListManager(
|
||||
repository = tangemPayTxHistoryRepository,
|
||||
dispatchers = dispatchers,
|
||||
customerWalletAddress = params.customerWalletAddress,
|
||||
txHistoryUiActions = this,
|
||||
txHistoryUiActions = params.uiActions,
|
||||
)
|
||||
|
||||
val uiState: StateFlow<TangemPayTxHistoryUM>
|
||||
|
|
@ -115,10 +112,6 @@ internal class TangemPayTxHistoryModel @Inject constructor(
|
|||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
override fun onTransactionClick(item: TangemPayTxHistoryItem) {
|
||||
Timber.d("onTransactionClick: $item")
|
||||
}
|
||||
|
||||
private fun getEmptyState(isBalanceHidden: Boolean): TangemPayTxHistoryUM.Empty {
|
||||
return TangemPayTxHistoryUM.Empty(isBalanceHidden = isBalanceHidden)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,202 @@
|
|||
package com.tangem.features.tangempay.model.transformers
|
||||
|
||||
import com.tangem.core.ui.components.label.entity.LabelStyle
|
||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.DateTimeFormatters
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUM
|
||||
import com.tangem.utils.StringsSigns
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.extensions.isPositive
|
||||
|
||||
internal object TangemPayTxHistoryDetailsConverter :
|
||||
Converter<TangemPayTxHistoryDetailsConverter.Input, TangemPayTxHistoryDetailsUM> {
|
||||
private val dateFormatter = DateTimeFormatters.getBestFormatterBySkeleton("dd MMMM")
|
||||
|
||||
override fun convert(value: Input): TangemPayTxHistoryDetailsUM {
|
||||
val transaction = value.item
|
||||
return TangemPayTxHistoryDetailsUM(
|
||||
title = transaction.extractDate(),
|
||||
iconState = transaction.extractIcon(),
|
||||
transactionTitle = transaction.extractTransactionTitle(),
|
||||
transactionSubtitle = transaction.extractTransactionSubtitle(),
|
||||
transactionAmount = transaction.extractAmount(),
|
||||
transactionAmountColor = value.item.extractAmountColor(),
|
||||
labelState = value.item.extractLabel(),
|
||||
notification = value.item.extractNotification(),
|
||||
buttonState = value.extractButtonState(),
|
||||
dismiss = value.onDismiss,
|
||||
)
|
||||
}
|
||||
|
||||
private fun TangemPayTxHistoryItem.extractDate(): TextReference {
|
||||
val date = DateTimeFormatters.formatDate(this.date, dateFormatter)
|
||||
val time = DateTimeFormatters.formatDate(this.date, DateTimeFormatters.timeFormatter)
|
||||
|
||||
return stringReference("$date ${StringsSigns.DOT} $time")
|
||||
}
|
||||
|
||||
private fun TangemPayTxHistoryItem.extractIcon(): ImageReference {
|
||||
return when (this) {
|
||||
is TangemPayTxHistoryItem.Fee -> ImageReference.Res(R.drawable.ic_percent_24)
|
||||
is TangemPayTxHistoryItem.Payment -> {
|
||||
if (this.amount.isPositive()) {
|
||||
ImageReference.Res(R.drawable.ic_arrow_down_24)
|
||||
} else {
|
||||
ImageReference.Res(R.drawable.ic_arrow_up_24)
|
||||
}
|
||||
}
|
||||
is TangemPayTxHistoryItem.Spend -> {
|
||||
val merchantIcon = this.enrichedMerchantIconUrl
|
||||
if (merchantIcon != null) {
|
||||
ImageReference.Url(merchantIcon)
|
||||
} else {
|
||||
ImageReference.Res(R.drawable.ic_category_24)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun TangemPayTxHistoryItem.extractTransactionTitle(): TextReference {
|
||||
return when (this) {
|
||||
is TangemPayTxHistoryItem.Fee -> resourceReference(R.string.tangem_pay_fee_title)
|
||||
is TangemPayTxHistoryItem.Spend -> stringReference(this.enrichedMerchantName ?: this.merchantName)
|
||||
is TangemPayTxHistoryItem.Payment -> if (this.amount.isPositive()) {
|
||||
resourceReference(R.string.tangem_pay_deposit)
|
||||
} else {
|
||||
resourceReference(R.string.tangem_pay_withdrawal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun TangemPayTxHistoryItem.extractTransactionSubtitle(): TextReference {
|
||||
return when (this) {
|
||||
is TangemPayTxHistoryItem.Fee -> resourceReference(R.string.tangem_pay_fee_subtitle)
|
||||
is TangemPayTxHistoryItem.Payment -> resourceReference(R.string.common_transfer)
|
||||
is TangemPayTxHistoryItem.Spend -> stringReference(this.enrichedMerchantCategory ?: this.merchantCategory)
|
||||
}
|
||||
}
|
||||
|
||||
private fun TangemPayTxHistoryItem.extractAmount(): String {
|
||||
return when (this) {
|
||||
is TangemPayTxHistoryItem.Fee,
|
||||
is TangemPayTxHistoryItem.Spend,
|
||||
-> {
|
||||
val amount = this.amount.format {
|
||||
fiat(
|
||||
fiatCurrencyCode = this@extractAmount.currency.currencyCode,
|
||||
fiatCurrencySymbol = this@extractAmount.currency.symbol,
|
||||
)
|
||||
}
|
||||
StringsSigns.MINUS + amount
|
||||
}
|
||||
is TangemPayTxHistoryItem.Payment -> {
|
||||
val amount = this.amount.format {
|
||||
fiat(
|
||||
fiatCurrencyCode = this@extractAmount.currency.currencyCode,
|
||||
fiatCurrencySymbol = this@extractAmount.currency.symbol,
|
||||
)
|
||||
}
|
||||
if (this.amount.isPositive()) {
|
||||
StringsSigns.PLUS + amount
|
||||
} else {
|
||||
StringsSigns.MINUS + amount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun TangemPayTxHistoryItem.extractAmountColor(): ColorReference {
|
||||
return when (this) {
|
||||
is TangemPayTxHistoryItem.Fee,
|
||||
is TangemPayTxHistoryItem.Spend,
|
||||
-> themedColor { TangemTheme.colors.text.primary1 }
|
||||
is TangemPayTxHistoryItem.Payment -> themedColor {
|
||||
if (this.amount.isPositive()) {
|
||||
TangemTheme.colors.text.accent
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun TangemPayTxHistoryItem.extractLabel(): LabelUM? {
|
||||
return when (this) {
|
||||
is TangemPayTxHistoryItem.Fee,
|
||||
is TangemPayTxHistoryItem.Payment,
|
||||
-> null
|
||||
is TangemPayTxHistoryItem.Spend -> when (this.status) {
|
||||
TangemPayTxHistoryItem.Status.COMPLETED -> LabelUM(
|
||||
text = resourceReference(R.string.tangem_pay_status_completed),
|
||||
style = LabelStyle.ACCENT,
|
||||
)
|
||||
TangemPayTxHistoryItem.Status.PENDING -> LabelUM(
|
||||
text = resourceReference(R.string.tangem_pay_status_pending),
|
||||
style = LabelStyle.REGULAR,
|
||||
icon = com.tangem.core.ui.R.drawable.ic_clock_24,
|
||||
)
|
||||
TangemPayTxHistoryItem.Status.DECLINED -> LabelUM(
|
||||
text = resourceReference(R.string.tangem_pay_status_declined),
|
||||
style = LabelStyle.WARNING,
|
||||
)
|
||||
TangemPayTxHistoryItem.Status.RESERVED,
|
||||
TangemPayTxHistoryItem.Status.UNKNOWN,
|
||||
-> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun TangemPayTxHistoryItem.extractNotification(): NotificationConfig? {
|
||||
return when (this) {
|
||||
is TangemPayTxHistoryItem.Payment -> null
|
||||
is TangemPayTxHistoryItem.Fee -> NotificationConfig(
|
||||
title = resourceReference(R.string.tangem_pay_transaction_fee_notification_text),
|
||||
subtitle = TextReference.EMPTY,
|
||||
iconResId = R.drawable.ic_token_info_24,
|
||||
)
|
||||
is TangemPayTxHistoryItem.Spend -> when (this.status) {
|
||||
TangemPayTxHistoryItem.Status.DECLINED -> NotificationConfig(
|
||||
title = resourceReference(R.string.tangem_pay_transaction_declined_notification_text),
|
||||
subtitle = TextReference.EMPTY,
|
||||
iconResId = R.drawable.ic_token_info_24,
|
||||
)
|
||||
TangemPayTxHistoryItem.Status.PENDING,
|
||||
TangemPayTxHistoryItem.Status.COMPLETED,
|
||||
TangemPayTxHistoryItem.Status.RESERVED,
|
||||
TangemPayTxHistoryItem.Status.UNKNOWN,
|
||||
-> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Input.extractButtonState(): TangemPayTxHistoryDetailsUM.ButtonState {
|
||||
return when (this.item) {
|
||||
is TangemPayTxHistoryItem.Fee -> TangemPayTxHistoryDetailsUM.ButtonState(
|
||||
text = resourceReference(R.string.tangem_pay_dispute),
|
||||
onClick = this.onDisputeClick,
|
||||
)
|
||||
is TangemPayTxHistoryItem.Spend -> TangemPayTxHistoryDetailsUM.ButtonState(
|
||||
text = resourceReference(R.string.tangem_pay_dispute),
|
||||
onClick = this.onDisputeClick,
|
||||
)
|
||||
is TangemPayTxHistoryItem.Payment -> TangemPayTxHistoryDetailsUM.ButtonState(
|
||||
text = resourceReference(R.string.tangem_pay_explore_transaction),
|
||||
onClick = { this.onExplorerClick(this.item.transactionHash) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class Input(
|
||||
val item: TangemPayTxHistoryItem,
|
||||
val onExplorerClick: (String?) -> Unit,
|
||||
val onDisputeClick: () -> Unit,
|
||||
val onDismiss: () -> Unit,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.tangem.features.tangempay.ui
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
internal fun RemoteIcon(url: String, modifier: Modifier = Modifier) {
|
||||
Icon(
|
||||
modifier = modifier.clip(CircleShape),
|
||||
painter = rememberAsyncImagePainter(url),
|
||||
contentDescription = null,
|
||||
tint = Color.Unspecified,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun LocalStaticIcon(@DrawableRes id: Int, iconSize: Dp, modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier.background(
|
||||
color = TangemTheme.colors.icon.secondary.copy(alpha = 0.1F),
|
||||
shape = CircleShape,
|
||||
),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(iconSize)
|
||||
.align(Alignment.Center),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,11 @@
|
|||
package com.tangem.features.tangempay.ui
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -15,7 +13,6 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
|
|
@ -24,7 +21,6 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.constraintlayout.compose.ChainStyle
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.CircleShimmer
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
|
|
@ -264,47 +260,24 @@ private fun Icon(state: TangemPayTransactionState, modifier: Modifier = Modifier
|
|||
if (state.iconUrl != null) {
|
||||
RemoteIcon(modifier = modifier, url = state.iconUrl)
|
||||
} else {
|
||||
LocalStaticIcon(modifier = modifier, id = R.drawable.ic_category_24)
|
||||
LocalStaticIcon(
|
||||
modifier = modifier,
|
||||
id = R.drawable.ic_category_24,
|
||||
iconSize = TangemTheme.dimens.size20,
|
||||
)
|
||||
}
|
||||
}
|
||||
is TangemPayTransactionState.Content.Fee -> LocalStaticIcon(modifier = modifier, id = R.drawable.ic_percent_24)
|
||||
is TangemPayTransactionState.Content.Fee -> LocalStaticIcon(
|
||||
modifier = modifier,
|
||||
id = R.drawable.ic_percent_24,
|
||||
iconSize = TangemTheme.dimens.size20,
|
||||
)
|
||||
is TangemPayTransactionState.Content.Payment -> LocalStaticIcon(
|
||||
modifier = modifier,
|
||||
id = if (state.isIncome) R.drawable.ic_arrow_down_24 else R.drawable.ic_arrow_up_24,
|
||||
iconSize = TangemTheme.dimens.size20,
|
||||
)
|
||||
is TangemPayTransactionState.Loading -> {
|
||||
CircleShimmer(modifier = modifier.size(TangemTheme.dimens.size40))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RemoteIcon(url: String, modifier: Modifier = Modifier) {
|
||||
Icon(
|
||||
modifier = modifier
|
||||
.size(TangemTheme.dimens.size40)
|
||||
.clip(CircleShape),
|
||||
painter = rememberAsyncImagePainter(url),
|
||||
contentDescription = null,
|
||||
tint = Color.Unspecified,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LocalStaticIcon(@DrawableRes id: Int, modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(TangemTheme.dimens.size40)
|
||||
.background(color = TangemTheme.colors.icon.secondary.copy(alpha = 0.1F), shape = CircleShape),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size20)
|
||||
.align(Alignment.Center),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
)
|
||||
is TangemPayTransactionState.Loading -> CircleShimmer(modifier = modifier)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,241 @@
|
|||
package com.tangem.features.tangempay.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
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.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.components.SpacerH32
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
|
||||
import com.tangem.core.ui.components.label.Label
|
||||
import com.tangem.core.ui.components.label.entity.LabelStyle
|
||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.extensions.ImageReference
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.themedColor
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUM
|
||||
|
||||
@Composable
|
||||
internal fun TangemPayTxHistoryDetailsContent(state: TangemPayTxHistoryDetailsUM) {
|
||||
TangemModalBottomSheet<TangemBottomSheetConfigContent.Empty>(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = state.dismiss,
|
||||
content = TangemBottomSheetConfigContent.Empty,
|
||||
),
|
||||
onBack = state.dismiss,
|
||||
containerColor = TangemTheme.colors.background.tertiary,
|
||||
title = {
|
||||
TangemModalBottomSheetTitle(
|
||||
title = state.title,
|
||||
endIconRes = R.drawable.ic_close_24,
|
||||
onEndClick = state.dismiss,
|
||||
)
|
||||
},
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(top = 24.dp)
|
||||
.size(88.dp),
|
||||
iconState = state.iconState,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 32.dp),
|
||||
text = state.transactionTitle.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 2.dp),
|
||||
text = state.transactionSubtitle.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
text = state.transactionAmount,
|
||||
style = TangemTheme.typography.head,
|
||||
color = state.transactionAmountColor.resolveReference(),
|
||||
)
|
||||
state.labelState?.let { Label(state = state.labelState, modifier = Modifier.padding(top = 12.dp)) }
|
||||
SpacerH32()
|
||||
state.notification?.let {
|
||||
Notification(
|
||||
config = state.notification,
|
||||
titleColor = TangemTheme.colors.text.tertiary,
|
||||
iconTint = TangemTheme.colors.icon.secondary,
|
||||
)
|
||||
}
|
||||
ButtonContainer(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
buttonState = state.buttonState,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ButtonContainer(buttonState: TangemPayTxHistoryDetailsUM.ButtonState, modifier: Modifier = Modifier) {
|
||||
SecondaryButton(modifier = modifier, text = buttonState.text.resolveReference(), onClick = buttonState.onClick)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Icon(iconState: ImageReference, modifier: Modifier = Modifier) {
|
||||
when (iconState) {
|
||||
is ImageReference.Res -> LocalStaticIcon(modifier = modifier, id = iconState.resId, iconSize = 40.dp)
|
||||
is ImageReference.Url -> RemoteIcon(modifier = modifier, url = iconState.url)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(device = Devices.PIXEL_7_PRO)
|
||||
@Preview(device = Devices.PIXEL_7_PRO, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun TangemPayTxHistoryDetailsContentPreview(
|
||||
@PreviewParameter(TangemPayTxHistoryDetailsUMProvider::class) state: TangemPayTxHistoryDetailsUM,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
TangemPayTxHistoryDetailsContent(state = state)
|
||||
}
|
||||
}
|
||||
|
||||
private class TangemPayTxHistoryDetailsUMProvider : CollectionPreviewParameterProvider<TangemPayTxHistoryDetailsUM>(
|
||||
listOf(
|
||||
TangemPayTxHistoryDetailsUM(
|
||||
title = stringReference("12 June • 12:40"),
|
||||
iconState = ImageReference.Res(R.drawable.ic_category_24),
|
||||
transactionTitle = stringReference("Starbucks"),
|
||||
transactionSubtitle = stringReference("Food and drinks"),
|
||||
transactionAmount = "-$5.86",
|
||||
transactionAmountColor = themedColor { TangemTheme.colors.text.primary1 },
|
||||
labelState = LabelUM(
|
||||
text = resourceReference(R.string.tangem_pay_status_pending),
|
||||
style = LabelStyle.REGULAR,
|
||||
icon = R.drawable.ic_clock_24,
|
||||
),
|
||||
notification = null,
|
||||
buttonState = TangemPayTxHistoryDetailsUM.ButtonState(
|
||||
text = stringReference("Dispute"),
|
||||
onClick = {},
|
||||
),
|
||||
dismiss = {},
|
||||
),
|
||||
TangemPayTxHistoryDetailsUM(
|
||||
title = stringReference("12 June • 12:40"),
|
||||
iconState = ImageReference.Res(R.drawable.ic_category_24),
|
||||
transactionTitle = stringReference("Starbucks"),
|
||||
transactionSubtitle = stringReference("Food and drinks"),
|
||||
transactionAmount = "-$5.86",
|
||||
transactionAmountColor = themedColor { TangemTheme.colors.text.warning },
|
||||
labelState = LabelUM(
|
||||
text = resourceReference(R.string.tangem_pay_status_declined),
|
||||
style = LabelStyle.WARNING,
|
||||
),
|
||||
notification = NotificationConfig(
|
||||
title = stringReference("The bank rejected this transaction request."),
|
||||
subtitle = TextReference.EMPTY,
|
||||
iconResId = R.drawable.ic_token_info_24,
|
||||
),
|
||||
buttonState = TangemPayTxHistoryDetailsUM.ButtonState(
|
||||
text = stringReference("Dispute"),
|
||||
onClick = {},
|
||||
),
|
||||
dismiss = {},
|
||||
),
|
||||
TangemPayTxHistoryDetailsUM(
|
||||
title = stringReference("12 June • 12:40"),
|
||||
iconState = ImageReference.Res(R.drawable.ic_category_24),
|
||||
transactionTitle = stringReference("Starbucks"),
|
||||
transactionSubtitle = stringReference("Food and drinks"),
|
||||
transactionAmount = "-$5.86",
|
||||
transactionAmountColor = themedColor { TangemTheme.colors.text.primary1 },
|
||||
labelState = LabelUM(
|
||||
text = resourceReference(R.string.tangem_pay_status_completed),
|
||||
style = LabelStyle.ACCENT,
|
||||
),
|
||||
notification = null,
|
||||
buttonState = TangemPayTxHistoryDetailsUM.ButtonState(
|
||||
text = stringReference("Dispute"),
|
||||
onClick = {},
|
||||
),
|
||||
dismiss = {},
|
||||
),
|
||||
TangemPayTxHistoryDetailsUM(
|
||||
title = stringReference("12 June • 12:40"),
|
||||
iconState = ImageReference.Res(R.drawable.ic_percent_24),
|
||||
transactionTitle = stringReference("Fee"),
|
||||
transactionSubtitle = stringReference("Service fee"),
|
||||
transactionAmount = "-$5.86",
|
||||
transactionAmountColor = themedColor { TangemTheme.colors.text.primary1 },
|
||||
labelState = null,
|
||||
notification = NotificationConfig(
|
||||
title = stringReference("This fee goes to cover the cost of handling your transfer."),
|
||||
subtitle = TextReference.EMPTY,
|
||||
iconResId = R.drawable.ic_token_info_24,
|
||||
),
|
||||
buttonState = TangemPayTxHistoryDetailsUM.ButtonState(
|
||||
text = stringReference("Dispute"),
|
||||
onClick = {},
|
||||
),
|
||||
dismiss = {},
|
||||
),
|
||||
TangemPayTxHistoryDetailsUM(
|
||||
title = stringReference("12 June • 12:40"),
|
||||
iconState = ImageReference.Res(R.drawable.ic_arrow_down_24),
|
||||
transactionTitle = stringReference("Deposit"),
|
||||
transactionSubtitle = stringReference("Transfers"),
|
||||
transactionAmount = "+$20",
|
||||
transactionAmountColor = themedColor { TangemTheme.colors.text.accent },
|
||||
labelState = null,
|
||||
notification = null,
|
||||
buttonState = TangemPayTxHistoryDetailsUM.ButtonState(
|
||||
text = stringReference("Explore transaction"),
|
||||
onClick = {},
|
||||
),
|
||||
dismiss = {},
|
||||
),
|
||||
TangemPayTxHistoryDetailsUM(
|
||||
title = stringReference("12 June • 12:40"),
|
||||
iconState = ImageReference.Res(R.drawable.ic_arrow_up_24),
|
||||
transactionTitle = stringReference("Withdrawal"),
|
||||
transactionSubtitle = stringReference("Transfers"),
|
||||
transactionAmount = "-$5.86",
|
||||
transactionAmountColor = themedColor { TangemTheme.colors.text.primary1 },
|
||||
labelState = null,
|
||||
notification = null,
|
||||
buttonState = TangemPayTxHistoryDetailsUM.ButtonState(
|
||||
text = stringReference("Explore transaction"),
|
||||
onClick = {},
|
||||
),
|
||||
dismiss = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -4,13 +4,15 @@ import com.tangem.core.ui.R
|
|||
import com.tangem.core.ui.components.bottomsheets.message.*
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.BottomSheetMessageV2
|
||||
import com.tangem.core.ui.message.bottomSheetMessage
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsErrorType
|
||||
|
||||
internal object TangemPayErrorMessageFactory {
|
||||
|
||||
fun createError(type: TangemPayDetailsErrorType, onDismiss: () -> Unit): MessageBottomSheetUMV2 {
|
||||
fun createErrorMessage(type: TangemPayDetailsErrorType): BottomSheetMessageV2 {
|
||||
return when (type) {
|
||||
TangemPayDetailsErrorType.Receive -> messageBottomSheetUM {
|
||||
TangemPayDetailsErrorType.Receive -> bottomSheetMessage {
|
||||
infoBlock {
|
||||
icon(R.drawable.img_attention_20) {
|
||||
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.Attention
|
||||
|
|
@ -20,7 +22,7 @@ internal object TangemPayErrorMessageFactory {
|
|||
}
|
||||
primaryButton {
|
||||
text = resourceReference(R.string.common_got_it)
|
||||
onClick { onDismiss() }
|
||||
onClick { closeBs() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -375,7 +375,12 @@ internal class WalletModel @Inject constructor(
|
|||
value = info,
|
||||
onClickIssue = ::issueOrder,
|
||||
onClickKyc = innerWalletRouter::openTangemPayOnboarding,
|
||||
openDetails = innerWalletRouter::openTangemPayDetails,
|
||||
openDetails = { config ->
|
||||
innerWalletRouter.openTangemPayDetails(
|
||||
userWalletId = stateHolder.getSelectedWalletId(),
|
||||
config = config,
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,8 +109,8 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
router.push(AppRoute.TangemPayOnboarding(AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding))
|
||||
}
|
||||
|
||||
override fun openTangemPayDetails(config: TangemPayDetailsConfig) {
|
||||
router.push(AppRoute.TangemPayDetails(config))
|
||||
override fun openTangemPayDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig) {
|
||||
router.push(AppRoute.TangemPayDetails(userWalletId = userWalletId, config = config))
|
||||
}
|
||||
|
||||
override fun openYieldSupplyBottomSheet(
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ internal interface InnerWalletRouter {
|
|||
|
||||
fun openTangemPayOnboarding()
|
||||
|
||||
fun openTangemPayDetails(config: TangemPayDetailsConfig)
|
||||
fun openTangemPayDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig)
|
||||
|
||||
/** Open BS abput yield supply active and all money deposited in AAVE */
|
||||
fun openYieldSupplyBottomSheet(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue