Updated on 2026-08-14

This commit is contained in:
Tangem 2025-04-07 12:53:01 +03:00
parent a398620b0d
commit 5e83a20d1a
27 changed files with 409 additions and 76 deletions

View file

@ -15,4 +15,5 @@ dependencies {
implementation(projects.core.res)
implementation(projects.domain.models)
implementation(projects.domain.wallets.models)
implementation(projects.domain.visa.models)
}

View file

@ -2,12 +2,43 @@ package com.tangem.domain.feedback
import com.tangem.domain.feedback.models.*
import com.tangem.domain.feedback.utils.breakLine
import com.tangem.domain.visa.model.VisaTxDetails
import com.tangem.domain.feedback.models.BlockchainInfo.Addresses as BlockchainAddresses
internal class FeedbackDataBuilder {
private val builder = StringBuilder()
fun addVisaTxInfo(txDetails: VisaTxDetails) {
builder.appendKeyValue("Type", txDetails.type)
builder.appendKeyValue("Status", txDetails.status)
builder.appendKeyValue("Blockchain amount", txDetails.blockchainAmount.toString())
builder.appendKeyValue("Transaction amount", txDetails.transactionAmount.toString())
builder.appendKeyValue("Currency code", txDetails.transactionCurrencyCode.toString())
builder.appendKeyValue("Merchant name", txDetails.merchantName)
builder.appendKeyValue("Merchant city", txDetails.merchantCity)
builder.appendKeyValue("Merchant country code", txDetails.merchantCountryCode)
builder.appendKeyValue("Merchant category code", txDetails.merchantCategoryCode)
builder.appendDelimiter()
builder.breakLine()
builder.append("Requests:")
txDetails.requests.forEach { request ->
builder.appendKeyValue("Type", request.requestType)
builder.appendKeyValue("Status", request.requestStatus)
builder.appendKeyValue("Blockchain amount", request.blockchainAmount.toString())
builder.appendKeyValue("Transaction amount", request.transactionAmount.toString())
builder.appendKeyValue("Currency code", request.txCurrencyCode.toString())
builder.appendKeyValue("Error code", request.errorCode.toString())
builder.appendKeyValue("Date", request.requestDate.toString())
builder.appendKeyValue("Transaction hash", request.txHash)
builder.appendKeyValue("Transaction status", request.txStatus)
builder.appendDelimiter()
builder.breakLine()
}
}
fun addUserWalletsInfo(userWalletsInfo: UserWalletsInfo) {
builder.appendKeyValue("User Wallet ID", userWalletsInfo.selectedUserWalletId)
builder.appendKeyValue("Total saved wallets", userWalletsInfo.totalUserWallets.toString())

View file

@ -2,7 +2,6 @@ package com.tangem.domain.feedback
import android.content.res.Resources
import com.tangem.core.res.getStringSafe
import com.tangem.domain.feedback.models.CardInfo
import com.tangem.domain.feedback.models.FeedbackEmail
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.feedback.repository.FeedbackRepository
@ -27,7 +26,7 @@ class SendFeedbackEmailUseCase(
suspend operator fun invoke(type: FeedbackEmailType) {
val email = FeedbackEmail(
address = getAddress(type.cardInfo),
address = getAddress(type),
subject = emailSubjectResolver.resolve(type),
message = createMessage(type),
// Temporally user data is not sent
@ -37,8 +36,12 @@ class SendFeedbackEmailUseCase(
feedbackRepository.sendEmail(email)
}
private fun getAddress(cardInfo: CardInfo?): String {
return if (cardInfo?.isStart2Coin == true) START2COIN_SUPPORT_EMAIL else TANGEM_SUPPORT_EMAIL
private fun getAddress(type: FeedbackEmailType): String {
return when {
type is FeedbackEmailType.Visa || type.cardInfo?.isVisa == true -> TANGEM_VISA_SUPPORT_EMAIL
type.cardInfo?.isStart2Coin == true -> START2COIN_SUPPORT_EMAIL
else -> TANGEM_SUPPORT_EMAIL
}
}
private suspend fun createMessage(type: FeedbackEmailType): String {
@ -61,12 +64,15 @@ class SendFeedbackEmailUseCase(
is FeedbackEmailType.CurrencyDescriptionError,
is FeedbackEmailType.PreActivatedWallet,
is FeedbackEmailType.CardAttestationFailed,
is FeedbackEmailType.Visa.Dispute,
-> this
is FeedbackEmailType.DirectUserRequest,
is FeedbackEmailType.RateCanBeBetter,
is FeedbackEmailType.StakingProblem,
is FeedbackEmailType.SwapProblem,
is FeedbackEmailType.TransactionSendingProblem,
is FeedbackEmailType.Visa.Activation,
is FeedbackEmailType.Visa.DirectUserRequest,
-> {
append(resources.getStringSafe(R.string.feedback_data_collection_message))
skipLine()

View file

@ -11,6 +11,7 @@ data class CardInfo(
val signedHashesList: List<SignedHashes>,
val isImported: Boolean,
val isStart2Coin: Boolean,
val isVisa: Boolean,
) {
data class SignedHashes(val curve: String, val total: String?)

View file

@ -1,5 +1,7 @@
package com.tangem.domain.feedback.models
import com.tangem.domain.visa.model.VisaTxDetails
/**
* Email feedback type
*
@ -52,4 +54,15 @@ sealed interface FeedbackEmailType {
data object CardAttestationFailed : FeedbackEmailType {
override val cardInfo: CardInfo? = null
}
sealed class Visa : FeedbackEmailType {
data class DirectUserRequest(override val cardInfo: CardInfo) : Visa()
data class Activation(override val cardInfo: CardInfo) : Visa()
data class Dispute(
val visaTxDetails: VisaTxDetails,
override val cardInfo: CardInfo,
) : Visa()
}
}

View file

@ -4,6 +4,7 @@ import com.tangem.domain.feedback.FeedbackDataBuilder
import com.tangem.domain.feedback.models.CardInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.feedback.repository.FeedbackRepository
import com.tangem.domain.visa.model.VisaTxDetails
/**
* Email message body resolver
@ -29,11 +30,20 @@ internal class EmailMessageBodyResolver(
is FeedbackEmailType.ScanningProblem,
is FeedbackEmailType.CardAttestationFailed,
-> addPhoneInfoBody()
is FeedbackEmailType.Visa.Activation -> addUserRequestBody(type.cardInfo)
is FeedbackEmailType.Visa.DirectUserRequest -> addUserRequestBody(type.cardInfo)
is FeedbackEmailType.Visa.Dispute -> addVisaRequestBody(type.cardInfo, type.visaTxDetails)
}
return build()
}
private suspend fun FeedbackDataBuilder.addVisaRequestBody(cardInfo: CardInfo, visaTxDetails: VisaTxDetails) {
addUserRequestBody(cardInfo)
addDelimiter()
addVisaTxInfo(visaTxDetails)
}
private suspend fun FeedbackDataBuilder.addUserRequestBody(cardInfo: CardInfo) {
addUserWalletsInfo(userWalletsInfo = feedbackRepository.getUserWalletsInfo(cardInfo.userWalletId))
addDelimiter()

View file

@ -20,6 +20,9 @@ internal class EmailMessageTitleResolver(private val resources: Resources) {
is FeedbackEmailType.DirectUserRequest,
is FeedbackEmailType.CurrencyDescriptionError,
is FeedbackEmailType.CardAttestationFailed,
is FeedbackEmailType.Visa.Activation,
is FeedbackEmailType.Visa.DirectUserRequest,
is FeedbackEmailType.Visa.Dispute,
-> R.string.feedback_preface_support
is FeedbackEmailType.RateCanBeBetter -> R.string.feedback_preface_rate_negative
is FeedbackEmailType.ScanningProblem -> R.string.feedback_preface_scan_failed

View file

@ -37,6 +37,9 @@ internal class EmailSubjectResolver(private val resources: Resources) {
resources.getStringSafe(R.string.feedback_token_description_error)
}
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}"
}
}
}

View file

@ -3,4 +3,5 @@ package com.tangem.domain.feedback.utils
internal const val GMAIL_MAX_FILE_SIZE = 24_900_000 // ≈ 25 MB
internal const val START2COIN_SUPPORT_EMAIL = "cardsupport@start2coin.com"
internal const val TANGEM_SUPPORT_EMAIL = "support@tangem.com"
internal const val TANGEM_SUPPORT_EMAIL = "support@tangem.com"
internal const val TANGEM_VISA_SUPPORT_EMAIL = "visa-support@tangem.com" // [REDACTED_TODO_COMMENT]