Updated on 2026-08-14

This commit is contained in:
Tangem 2024-06-25 20:58:35 +08:00
parent fe1d2342f4
commit 24848624e9
49 changed files with 414 additions and 182 deletions

View file

@ -13,5 +13,6 @@ dependencies {
implementation(deps.jodatime)
implementation(projects.core.res)
implementation(projects.domain.models)
implementation(projects.domain.wallets.models)
}

View file

@ -0,0 +1,8 @@
package com.tangem.domain.feedback
import com.tangem.domain.feedback.models.FeedbackEmailType
interface FeedbackManager {
fun sendEmail(type: FeedbackEmailType)
}

View file

@ -0,0 +1,23 @@
package com.tangem.domain.feedback
import arrow.core.Either
import arrow.core.Either.Companion.catch
import com.tangem.domain.feedback.models.CardInfo
import com.tangem.domain.feedback.repository.FeedbackRepository
import com.tangem.domain.models.scan.ScanResponse
/**
* UseCase for creating 'CardInfo'
*
* @property feedbackRepository feedback repository
*
[REDACTED_AUTHOR]
*/
class GetCardInfoUseCase(
private val feedbackRepository: FeedbackRepository,
) {
suspend operator fun invoke(scanResponse: ScanResponse): Either<Throwable, CardInfo> {
return catch { feedbackRepository.getCardInfo(scanResponse) }
}
}

View file

@ -25,23 +25,21 @@ class GetFeedbackEmailUseCase(
private val emailMessageBodyResolver = EmailMessageBodyResolver(feedbackRepository)
suspend operator fun invoke(type: FeedbackEmailType): FeedbackEmail {
val cardInfo = feedbackRepository.getCardInfo()
val formattedLogs = AppLogsFormatter().format(appLogs = feedbackRepository.getAppLogs())
return FeedbackEmail(
address = getAddress(cardInfo),
subject = emailSubjectResolver.resolve(type, cardInfo),
message = createMessage(type, cardInfo),
address = getAddress(type.cardInfo),
subject = emailSubjectResolver.resolve(type),
message = createMessage(type),
file = feedbackRepository.createLogFile(logs = formattedLogs),
)
}
private fun getAddress(cardInfo: CardInfo): String {
return if (cardInfo.isStart2Coin) START2COIN_SUPPORT_EMAIL else TANGEM_SUPPORT_EMAIL
private fun getAddress(cardInfo: CardInfo?): String {
return if (cardInfo?.isStart2Coin == true) START2COIN_SUPPORT_EMAIL else TANGEM_SUPPORT_EMAIL
}
private suspend fun createMessage(type: FeedbackEmailType, cardInfo: CardInfo): String {
private suspend fun createMessage(type: FeedbackEmailType): String {
return StringBuilder().apply {
val title = emailMessageTitleResolver.resolve(type)
append(title)
@ -50,9 +48,7 @@ class GetFeedbackEmailUseCase(
appendDisclaimerIfNeeded(type)
skipLine()
val body = emailMessageBodyResolver.resolve(type, cardInfo)
val body = emailMessageBodyResolver.resolve(type)
append(body)
}.toString()
}
@ -62,6 +58,7 @@ class GetFeedbackEmailUseCase(
this
} else {
append(resources.getString(R.string.feedback_data_collection_message))
skipLine()
}
}
}

View file

@ -1,6 +1,9 @@
package com.tangem.domain.feedback.models
import com.tangem.domain.wallets.models.UserWalletId
data class CardInfo(
val userWalletId: UserWalletId?,
val cardId: String,
val firmwareVersion: String,
val cardBlockchain: String?,

View file

@ -7,15 +7,19 @@ package com.tangem.domain.feedback.models
*/
sealed interface FeedbackEmailType {
val cardInfo: CardInfo?
/** User initiate request yourself. Example, button on DetailsScreen or OnboardingScreen */
data object DirectUserRequest : FeedbackEmailType
data class DirectUserRequest(override val cardInfo: CardInfo) : FeedbackEmailType
/** User rate the app as "can be better" */
data object RateCanBeBetter : FeedbackEmailType
data class RateCanBeBetter(override val cardInfo: CardInfo) : FeedbackEmailType
/** User has problem with scanning */
data object ScanningProblem : FeedbackEmailType
data object ScanningProblem : FeedbackEmailType {
override val cardInfo: CardInfo? = null
}
/** User has problem with sending transaction */
data object TransactionSendingProblem : FeedbackEmailType
data class TransactionSendingProblem(override val cardInfo: CardInfo) : FeedbackEmailType
}

View file

@ -1,23 +1,29 @@
package com.tangem.domain.feedback.repository
import com.tangem.domain.feedback.models.*
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.models.UserWalletId
import java.io.File
interface FeedbackRepository {
suspend fun getUserWalletsInfo(): UserWalletsInfo
suspend fun getCardInfo(scanResponse: ScanResponse): CardInfo
suspend fun getCardInfo(): CardInfo
suspend fun getUserWalletsInfo(userWalletId: UserWalletId?): UserWalletsInfo
suspend fun getBlockchainInfoList(): List<BlockchainInfo>
suspend fun getBlockchainInfo(blockchainId: String, derivationPath: String?): BlockchainInfo?
suspend fun getBlockchainInfoList(userWalletId: UserWalletId): List<BlockchainInfo>
fun getPhoneInfo(): PhoneInfo
suspend fun getBlockchainInfo(
userWalletId: UserWalletId,
blockchainId: String,
derivationPath: String?,
): BlockchainInfo?
fun saveBlockchainErrorInfo(error: BlockchainErrorInfo)
suspend fun getBlockchainErrorInfo(): BlockchainErrorInfo?
suspend fun getBlockchainErrorInfo(userWalletId: UserWalletId): BlockchainErrorInfo?
suspend fun getAppLogs(): List<AppLogModel>

View file

@ -16,25 +16,33 @@ internal class EmailMessageBodyResolver(
private val feedbackRepository: FeedbackRepository,
) {
/** Resolve email message body by [type] using [cardInfo] */
suspend fun resolve(type: FeedbackEmailType, cardInfo: CardInfo): String = with(FeedbackDataBuilder()) {
/** Resolve email message body by [type] */
suspend fun resolve(type: FeedbackEmailType): String = with(FeedbackDataBuilder()) {
when (type) {
FeedbackEmailType.DirectUserRequest -> addUserRequestBody(cardInfo)
FeedbackEmailType.RateCanBeBetter -> addCardAndPhoneInfo(cardInfo)
FeedbackEmailType.ScanningProblem -> addScanningProblemBody()
FeedbackEmailType.TransactionSendingProblem -> addTransactionSendingProblemBody(cardInfo)
is FeedbackEmailType.DirectUserRequest -> addUserRequestBody(type.cardInfo)
is FeedbackEmailType.RateCanBeBetter -> addCardAndPhoneInfo(type.cardInfo)
is FeedbackEmailType.ScanningProblem -> addScanningProblemBody()
is FeedbackEmailType.TransactionSendingProblem -> addTransactionSendingProblemBody(type.cardInfo)
}
return build()
}
private suspend fun FeedbackDataBuilder.addUserRequestBody(cardInfo: CardInfo) {
addUserWalletsInfo(userWalletsInfo = feedbackRepository.getUserWalletsInfo())
addUserWalletsInfo(userWalletsInfo = feedbackRepository.getUserWalletsInfo(cardInfo.userWalletId))
addDelimiter()
addCardInfo(cardInfo)
addDelimiter()
addBlockchainInfoList(blockchainInfoList = feedbackRepository.getBlockchainInfoList())
addDelimiter()
if (cardInfo.userWalletId != null) {
val blockchainInfoList = feedbackRepository.getBlockchainInfoList(cardInfo.userWalletId)
if (blockchainInfoList.isNotEmpty()) {
addBlockchainInfoList(blockchainInfoList = blockchainInfoList)
addDelimiter()
}
}
addPhoneInfo(phoneInfo = feedbackRepository.getPhoneInfo())
}
@ -46,9 +54,11 @@ internal class EmailMessageBodyResolver(
addCardInfo(cardInfo)
addDelimiter()
val blockchainError = feedbackRepository.getBlockchainErrorInfo()
val userWalletId = requireNotNull(cardInfo.userWalletId) { "UserWalletId must be not null" }
val blockchainError = feedbackRepository.getBlockchainErrorInfo(userWalletId = userWalletId)
val blockchainInfo = blockchainError?.let {
feedbackRepository.getBlockchainInfo(
userWalletId = userWalletId,
blockchainId = blockchainError.blockchainId,
derivationPath = blockchainError.derivationPath,
)

View file

@ -16,10 +16,10 @@ internal class EmailMessageTitleResolver(private val resources: Resources) {
/** Resolve email message title by [type] */
fun resolve(type: FeedbackEmailType): String {
return when (type) {
FeedbackEmailType.DirectUserRequest -> R.string.feedback_preface_support
FeedbackEmailType.RateCanBeBetter -> R.string.feedback_preface_rate_negative
FeedbackEmailType.ScanningProblem -> R.string.feedback_preface_scan_failed
FeedbackEmailType.TransactionSendingProblem -> R.string.feedback_preface_tx_failed
is FeedbackEmailType.DirectUserRequest -> R.string.feedback_preface_support
is FeedbackEmailType.RateCanBeBetter -> R.string.feedback_preface_rate_negative
is FeedbackEmailType.ScanningProblem -> R.string.feedback_preface_scan_failed
is FeedbackEmailType.TransactionSendingProblem -> R.string.feedback_preface_tx_failed
}
.let(resources::getString)
}

View file

@ -2,7 +2,6 @@ package com.tangem.domain.feedback.utils
import android.content.res.Resources
import com.tangem.domain.feedback.R
import com.tangem.domain.feedback.models.CardInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
/**
@ -14,19 +13,19 @@ import com.tangem.domain.feedback.models.FeedbackEmailType
*/
internal class EmailSubjectResolver(private val resources: Resources) {
/** Resolve email message body by [type] using [cardInfo] */
fun resolve(type: FeedbackEmailType, cardInfo: CardInfo): String {
/** Resolve email message body by [type] */
fun resolve(type: FeedbackEmailType): String {
return when (type) {
FeedbackEmailType.DirectUserRequest -> {
if (cardInfo.isStart2Coin) {
is FeedbackEmailType.DirectUserRequest -> {
if (type.cardInfo.isStart2Coin) {
R.string.feedback_subject_support
} else {
R.string.feedback_subject_support_tangem
}
}
FeedbackEmailType.RateCanBeBetter -> R.string.feedback_subject_rate_negative
FeedbackEmailType.ScanningProblem -> R.string.feedback_subject_scan_failed
FeedbackEmailType.TransactionSendingProblem -> R.string.feedback_subject_tx_failed
is FeedbackEmailType.RateCanBeBetter -> R.string.feedback_subject_rate_negative
is FeedbackEmailType.ScanningProblem -> R.string.feedback_subject_scan_failed
is FeedbackEmailType.TransactionSendingProblem -> R.string.feedback_subject_tx_failed
}
.let(resources::getString)
}