Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-01 19:21:37 +03:00
parent 6312034cf9
commit 17d7fb3c02
36 changed files with 311 additions and 301 deletions

View file

@ -29,7 +29,7 @@ import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
@ -107,7 +107,7 @@ interface ApplicationEntryPoint {
fun getSendFeedbackEmailUseCase(): SendFeedbackEmailUseCase
fun getGetCardInfoUseCase(): GetCardInfoUseCase
fun getWalletMetaInfoUseCase(): GetWalletMetaInfoUseCase
fun getUrlOpener(): UrlOpener

View file

@ -47,7 +47,7 @@ import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.common.LogConfig
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
@ -166,8 +166,8 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase
get() = entryPoint.getSendFeedbackEmailUseCase()
private val getCardInfoUseCase: GetCardInfoUseCase
get() = entryPoint.getGetCardInfoUseCase()
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase
get() = entryPoint.getWalletMetaInfoUseCase()
private val urlOpener
get() = entryPoint.getUrlOpener()
@ -359,7 +359,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
settingsRepository = settingsRepository,
blockchainSDKFactory = blockchainSDKFactory,
sendFeedbackEmailUseCase = sendFeedbackEmailUseCase,
getCardInfoUseCase = getCardInfoUseCase,
getWalletMetaInfoUseCase = getWalletMetaInfoUseCase,
issuersConfigStorage = issuersConfigStorage,
urlOpener = urlOpener,
shareManager = shareManager,

View file

@ -1,7 +1,7 @@
package com.tangem.tap.di.domain
import android.content.Context
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.repository.FeedbackRepository
@ -18,8 +18,8 @@ internal object FeedbackDomainModule {
@Provides
@Singleton
fun provideGetCardInfoUseCase(feedbackRepository: FeedbackRepository): GetCardInfoUseCase {
return GetCardInfoUseCase(feedbackRepository = feedbackRepository)
fun provideGetCardInfoUseCase(feedbackRepository: FeedbackRepository): GetWalletMetaInfoUseCase {
return GetWalletMetaInfoUseCase(feedbackRepository = feedbackRepository)
}
@Provides

View file

@ -265,7 +265,7 @@ internal class LegacyScanProcessor @Inject constructor(
onOk = { mainScope.launch { onSuccess() } },
onSupportClick = {
val cardInfo =
store.inject(DaggerGraphState::getCardInfoUseCase).invoke(scanResponse).getOrNull()
store.inject(DaggerGraphState::getWalletMetaInfoUseCase).invoke(scanResponse).getOrNull()
?: error("CardInfo must be not null")
scope.launch {

View file

@ -30,7 +30,7 @@ object WalletActivationErrorDialog {
val scanResponse = store.state.globalState.scanResponse
?: error("ScanResponse must be not null")
val cardInfo = store.inject(DaggerGraphState::getCardInfoUseCase).invoke(scanResponse).getOrNull()
val cardInfo = store.inject(DaggerGraphState::getWalletMetaInfoUseCase).invoke(scanResponse).getOrNull()
?: error("CardInfo must be not null")
scope.launch {

View file

@ -22,7 +22,7 @@ import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
@ -63,7 +63,7 @@ data class DaggerGraphState(
val settingsRepository: SettingsRepository? = null,
val blockchainSDKFactory: BlockchainSDKFactory? = null,
val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase? = null,
val getCardInfoUseCase: GetCardInfoUseCase? = null,
val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase? = null,
val issuersConfigStorage: IssuersConfigStorage? = null,
val urlOpener: UrlOpener? = null,
val shareManager: ShareManager? = null,

View file

@ -1,14 +1,13 @@
package com.tangem.common.routing
import android.os.Bundle
import com.tangem.common.routing.AppRoute.ManageTokens.Source
import com.tangem.common.routing.bundle.RouteBundleParams
import com.tangem.common.routing.bundle.bundle
import com.tangem.common.routing.entity.InitScreenLaunchMode
import com.tangem.common.routing.entity.SerializableIntent
import com.tangem.core.decompose.navigation.Route
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.feedback.models.CardInfo
import com.tangem.domain.feedback.models.WalletMetaInfo
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrency
@ -83,8 +82,8 @@ sealed class AppRoute(val path: String) : Route {
@Serializable
data class Usedesk(
val cardInfo: CardInfo,
) : AppRoute(path = "/usedesk/${cardInfo.cardId}")
val walletMetaInfo: WalletMetaInfo,
) : AppRoute(path = "/usedesk/${walletMetaInfo.userWalletId}")
@Serializable
data class CardSettings(

View file

@ -10,7 +10,6 @@ android {
}
dependencies {
// region AndroidX libraries
implementation(deps.androidx.datastore)
// endregion
@ -37,6 +36,8 @@ dependencies {
// endregion
// Feature modules
implementation(projects.features.hotWallet.api)
implementation(projects.domain.feedback)
implementation(projects.domain.feedback.models)

View file

@ -4,12 +4,14 @@ import android.os.Build
import com.tangem.blockchain.common.Blockchain
import com.tangem.core.navigation.email.EmailSender
import com.tangem.data.feedback.converters.BlockchainInfoConverter
import com.tangem.data.feedback.converters.CardInfoConverter
import com.tangem.data.feedback.converters.WalletMetaInfoConverter
import com.tangem.datasource.local.logs.AppLogsStore
import com.tangem.datasource.local.walletmanager.WalletManagersStore
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.domain.feedback.models.*
import com.tangem.domain.feedback.repository.FeedbackRepository
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
@ -24,14 +26,19 @@ import java.io.File
*
* @property appLogsStore app logs store
* @property userWalletsListManager user wallets list manager
* @property useNewUserWalletsRepository flag to use new user wallets repository
* @property userWalletsListRepository user wallets repository
* @property walletManagersStore wallet managers store
* @property emailSender email sender
* @property appVersionProvider app version provider
*
[REDACTED_AUTHOR]
*/
@Suppress("LongParameterList")
internal class DefaultFeedbackRepository(
private val appLogsStore: AppLogsStore,
private val useNewUserWalletsRepository: Boolean,
private val userWalletsListRepository: UserWalletsListRepository,
private val userWalletsListManager: UserWalletsListManager,
private val walletManagersStore: WalletManagersStore,
private val emailSender: EmailSender,
@ -41,12 +48,21 @@ internal class DefaultFeedbackRepository(
private val blockchainsErrors = MutableStateFlow<Map<UserWalletId, BlockchainErrorInfo>>(emptyMap())
override fun getCardInfo(scanResponse: ScanResponse) = CardInfoConverter.convert(value = scanResponse)
override suspend fun getUserWalletMetaInfo(userWalletId: UserWalletId): WalletMetaInfo {
val userWallet = getUserWalletById(userWalletId)
return userWallet?.let {
WalletMetaInfoConverter.convert(it)
} ?: WalletMetaInfo(userWalletId)
}
override fun getUserWalletMetaInfo(scanResponse: ScanResponse): WalletMetaInfo {
return WalletMetaInfoConverter.convert(value = scanResponse)
}
override fun getUserWalletsInfo(userWalletId: UserWalletId?): UserWalletsInfo {
return UserWalletsInfo(
selectedUserWalletId = userWalletId?.stringValue ?: "card isn't activated",
totalUserWallets = userWalletsListManager.walletsCount,
totalUserWallets = totalUserWallets(),
)
}
@ -108,4 +124,20 @@ internal class DefaultFeedbackRepository(
),
)
}
private suspend fun getUserWalletById(userWalletId: UserWalletId): UserWallet? {
return if (useNewUserWalletsRepository) {
userWalletsListRepository.userWalletsSync().find { it.walletId == userWalletId }
} else {
userWalletsListManager.userWalletsSync.find { it.walletId == userWalletId }
}
}
private fun totalUserWallets(): Int {
return if (useNewUserWalletsRepository) {
userWalletsListRepository.userWallets.value?.size ?: 0
} else {
userWalletsListManager.walletsCount
}
}
}

View file

@ -1,41 +0,0 @@
package com.tangem.data.feedback.converters
import com.tangem.domain.card.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.card.common.TapWorkarounds.isVisa
import com.tangem.domain.card.common.util.getBackupCardsCount
import com.tangem.domain.feedback.models.CardInfo
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.utils.converter.Converter
/**
* Converter from [ScanResponse] to [CardInfo]
*
[REDACTED_AUTHOR]
*/
internal object CardInfoConverter : Converter<ScanResponse, CardInfo> {
override fun convert(value: ScanResponse): CardInfo {
return with(value) {
CardInfo(
userWalletId = createUserWalletId(scanResponse = value),
cardId = card.cardId,
cardsCount = value.getBackupCardsCount()?.toString() ?: "0",
firmwareVersion = card.firmwareVersion.stringValue,
cardBlockchain = walletData?.blockchain,
signedHashesList = card.wallets.map {
CardInfo.SignedHashes(curve = it.curve.curve, total = it.totalSignedHashes?.toString())
},
isImported = value.card.wallets.any(CardDTO.Wallet::isImported),
isStart2Coin = value.card.isStart2Coin,
isVisa = value.card.isVisa,
)
}
}
private fun createUserWalletId(scanResponse: ScanResponse): UserWalletId? {
return UserWalletIdBuilder.scanResponse(scanResponse = scanResponse).build()
}
}

View file

@ -0,0 +1,66 @@
package com.tangem.data.feedback.converters
import com.tangem.domain.card.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.card.common.TapWorkarounds.isVisa
import com.tangem.domain.card.common.util.getBackupCardsCount
import com.tangem.domain.feedback.models.WalletMetaInfo
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.utils.converter.Converter
/**
* Converter from [UserWallet] to [WalletMetaInfo]
*
[REDACTED_AUTHOR]
*/
internal object WalletMetaInfoConverter : Converter<UserWallet, WalletMetaInfo> {
override fun convert(value: UserWallet): WalletMetaInfo {
return when (value) {
is UserWallet.Cold -> {
WalletMetaInfo(
userWalletId = value.walletId,
cardId = value.scanResponse.card.cardId,
cardsCount = value.getBackupCardsCount()?.toString() ?: "0",
firmwareVersion = value.scanResponse.card.firmwareVersion.stringValue,
cardBlockchain = value.scanResponse.walletData?.blockchain,
signedHashesList = value.scanResponse.card.wallets.map {
WalletMetaInfo.SignedHashes(curve = it.curve.curve, total = it.totalSignedHashes?.toString())
},
isImported = value.scanResponse.card.wallets.any(CardDTO.Wallet::isImported),
isStart2Coin = value.scanResponse.card.isStart2Coin,
isVisa = value.scanResponse.card.isVisa,
)
}
is UserWallet.Hot -> {
WalletMetaInfo(
userWalletId = value.walletId,
hotWalletIsBackedUp = value.backedUp,
)
}
}
}
fun convert(value: ScanResponse): WalletMetaInfo {
return WalletMetaInfo(
userWalletId = createUserWalletId(value),
cardId = value.card.cardId,
cardsCount = value.getBackupCardsCount()?.toString() ?: "0",
firmwareVersion = value.card.firmwareVersion.stringValue,
cardBlockchain = value.walletData?.blockchain,
signedHashesList = value.card.wallets.map {
WalletMetaInfo.SignedHashes(curve = it.curve.curve, total = it.totalSignedHashes?.toString())
},
isImported = value.card.wallets.any(CardDTO.Wallet::isImported),
isStart2Coin = value.card.isStart2Coin,
isVisa = value.card.isVisa,
)
}
private fun createUserWalletId(scanResponse: ScanResponse): UserWalletId? {
return UserWalletIdBuilder.scanResponse(scanResponse = scanResponse).build()
}
}

View file

@ -6,10 +6,12 @@ import com.tangem.data.feedback.DefaultFeedbackFeatureToggles
import com.tangem.data.feedback.DefaultFeedbackRepository
import com.tangem.datasource.local.logs.AppLogsStore
import com.tangem.datasource.local.walletmanager.WalletManagersStore
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.domain.feedback.repository.FeedbackFeatureToggles
import com.tangem.domain.feedback.repository.FeedbackRepository
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.utils.version.AppVersionProvider
import dagger.Module
import dagger.Provides
@ -26,6 +28,8 @@ internal object FeedbackModule {
fun provideFeedbackRepository(
appLogsStore: AppLogsStore,
userWalletsListManager: UserWalletsListManager,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
walletManagersStore: WalletManagersStore,
emailSender: EmailSender,
appVersionProvider: AppVersionProvider,
@ -37,6 +41,8 @@ internal object FeedbackModule {
walletManagersStore = walletManagersStore,
emailSender = emailSender,
appVersionProvider = appVersionProvider,
userWalletsListRepository = userWalletsListRepository,
useNewUserWalletsRepository = hotWalletFeatureToggles.isHotWalletEnabled,
getSelectedWalletUseCase = getSelectedWalletUseCase,
)
}

View file

@ -1,21 +0,0 @@
package com.tangem.domain.feedback.models
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.serialization.Serializable
@Serializable
data class CardInfo(
val userWalletId: UserWalletId?,
val cardId: String,
val firmwareVersion: String,
val cardsCount: String,
val cardBlockchain: String?,
val signedHashesList: List<SignedHashes>,
val isImported: Boolean,
val isStart2Coin: Boolean,
val isVisa: Boolean,
) {
@Serializable
data class SignedHashes(val curve: String, val total: String?)
}

View file

@ -9,32 +9,32 @@ import com.tangem.domain.visa.model.VisaTxDetails
*/
sealed interface FeedbackEmailType {
val cardInfo: CardInfo?
val walletMetaInfo: WalletMetaInfo?
/** User initiate request yourself. Example, button on DetailsScreen or OnboardingScreen */
data class DirectUserRequest(override val cardInfo: CardInfo) : FeedbackEmailType
data class DirectUserRequest(override val walletMetaInfo: WalletMetaInfo) : FeedbackEmailType
/** User rate the app as "can be better" */
data class RateCanBeBetter(override val cardInfo: CardInfo) : FeedbackEmailType
data class RateCanBeBetter(override val walletMetaInfo: WalletMetaInfo) : FeedbackEmailType
/** User has problem with scanning */
data object ScanningProblem : FeedbackEmailType {
override val cardInfo: CardInfo? = null
override val walletMetaInfo: WalletMetaInfo? = null
}
/** User has problem with sending transaction */
data class TransactionSendingProblem(override val cardInfo: CardInfo) : FeedbackEmailType
data class TransactionSendingProblem(override val walletMetaInfo: WalletMetaInfo) : FeedbackEmailType
/** User has problem with staking */
data class StakingProblem(
override val cardInfo: CardInfo,
override val walletMetaInfo: WalletMetaInfo,
val validatorName: String?,
val transactionTypes: List<String>,
val unsignedTransactions: List<String?>,
) : FeedbackEmailType
data class SwapProblem(
override val cardInfo: CardInfo,
override val walletMetaInfo: WalletMetaInfo,
val providerName: String,
val txId: String,
) : FeedbackEmailType
@ -46,23 +46,23 @@ sealed interface FeedbackEmailType {
* @property currencyName currency name
*/
data class CurrencyDescriptionError(val currencyId: String, val currencyName: String) : FeedbackEmailType {
override val cardInfo: CardInfo? = null
override val walletMetaInfo: WalletMetaInfo? = null
}
data class PreActivatedWallet(override val cardInfo: CardInfo) : FeedbackEmailType
data class PreActivatedWallet(override val walletMetaInfo: WalletMetaInfo) : FeedbackEmailType
data object CardAttestationFailed : FeedbackEmailType {
override val cardInfo: CardInfo? = null
override val walletMetaInfo: WalletMetaInfo? = null
}
sealed class Visa : FeedbackEmailType {
data class DirectUserRequest(override val cardInfo: CardInfo) : Visa()
data class DirectUserRequest(override val walletMetaInfo: WalletMetaInfo) : Visa()
data class Activation(override val cardInfo: CardInfo) : Visa()
data class Activation(override val walletMetaInfo: WalletMetaInfo) : Visa()
data class Dispute(
val visaTxDetails: VisaTxDetails,
override val cardInfo: CardInfo,
override val walletMetaInfo: WalletMetaInfo,
) : Visa()
}
}

View file

@ -0,0 +1,22 @@
package com.tangem.domain.feedback.models
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.serialization.Serializable
@Serializable
data class WalletMetaInfo(
val userWalletId: UserWalletId?,
val hotWalletIsBackedUp: Boolean? = null,
val cardId: String? = null,
val firmwareVersion: String? = null,
val cardsCount: String? = null,
val cardBlockchain: String? = null,
val signedHashesList: List<SignedHashes>? = null,
val isImported: Boolean? = null,
val isStart2Coin: Boolean? = null,
val isVisa: Boolean? = null,
) {
@Serializable
data class SignedHashes(val curve: String, val total: String?)
}

View file

@ -44,13 +44,14 @@ internal class FeedbackDataBuilder {
builder.appendKeyValue("Total saved wallets", userWalletsInfo.totalUserWallets.toString())
}
fun addCardInfo(cardInfo: CardInfo) {
builder.appendKeyValue("Card ID", cardInfo.cardId)
builder.appendKeyValue("Firmware version", cardInfo.firmwareVersion)
builder.appendKeyValue("Linked cards count", cardInfo.cardsCount)
builder.appendKeyValue("Has seed phrase", cardInfo.isImported.toString())
builder.appendKeyValue("Card Blockchain", cardInfo.cardBlockchain)
builder.appendSignedHashes(cardInfo.signedHashesList)
fun addUserWalletMetaInfo(walletMetaInfo: WalletMetaInfo) {
builder.appendKeyValue("Mobile Wallet is backed up", walletMetaInfo.hotWalletIsBackedUp?.toString())
builder.appendKeyValue("Card ID", walletMetaInfo.cardId)
builder.appendKeyValue("Firmware version", walletMetaInfo.firmwareVersion)
builder.appendKeyValue("Linked cards count", walletMetaInfo.cardsCount)
builder.appendKeyValue("Has seed phrase", walletMetaInfo.isImported?.toString())
builder.appendKeyValue("Card Blockchain", walletMetaInfo.cardBlockchain)
walletMetaInfo.signedHashesList?.let { builder.appendSignedHashes(it) }
}
fun addBlockchainInfoList(blockchainInfoList: List<BlockchainInfo>) {
@ -146,7 +147,7 @@ internal class FeedbackDataBuilder {
append("$keyValuePrefix$value\n")
}
private fun StringBuilder.appendSignedHashes(signedHashesList: List<CardInfo.SignedHashes>) {
private fun StringBuilder.appendSignedHashes(signedHashesList: List<WalletMetaInfo.SignedHashes>) {
signedHashesList.forEach {
appendKeyValue("Signed hashes [${it.curve}]", it.total)
}

View file

@ -1,23 +0,0 @@
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,
) {
operator fun invoke(scanResponse: ScanResponse): Either<Throwable, CardInfo> = catch {
feedbackRepository.getCardInfo(scanResponse)
}
}

View file

@ -0,0 +1,28 @@
package com.tangem.domain.feedback
import arrow.core.Either
import arrow.core.Either.Companion.catch
import com.tangem.domain.feedback.models.WalletMetaInfo
import com.tangem.domain.feedback.repository.FeedbackRepository
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWalletId
/**
* UseCase for creating 'UserWalletMetaInfo' from [UserWalletId] or [ScanResponse]
*
* @property feedbackRepository feedback repository
*
[REDACTED_AUTHOR]
*/
class GetWalletMetaInfoUseCase(
private val feedbackRepository: FeedbackRepository,
) {
suspend operator fun invoke(userWalletId: UserWalletId): Either<Throwable, WalletMetaInfo> = catch {
feedbackRepository.getUserWalletMetaInfo(userWalletId)
}
operator fun invoke(scanResponse: ScanResponse): Either<Throwable, WalletMetaInfo> = catch {
feedbackRepository.getUserWalletMetaInfo(scanResponse)
}
}

View file

@ -37,8 +37,8 @@ class SendFeedbackEmailUseCase(
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
type is FeedbackEmailType.Visa || type.walletMetaInfo?.isVisa == true -> TANGEM_VISA_SUPPORT_EMAIL
type.walletMetaInfo?.isStart2Coin == true -> START2COIN_SUPPORT_EMAIL
else -> TANGEM_SUPPORT_EMAIL
}
}

View file

@ -7,7 +7,9 @@ import java.io.File
interface FeedbackRepository {
fun getCardInfo(scanResponse: ScanResponse): CardInfo
suspend fun getUserWalletMetaInfo(userWalletId: UserWalletId): WalletMetaInfo
fun getUserWalletMetaInfo(scanResponse: ScanResponse): WalletMetaInfo
fun getUserWalletsInfo(userWalletId: UserWalletId?): UserWalletsInfo

View file

@ -1,7 +1,7 @@
package com.tangem.domain.feedback.utils
import com.tangem.domain.feedback.FeedbackDataBuilder
import com.tangem.domain.feedback.models.CardInfo
import com.tangem.domain.feedback.models.WalletMetaInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.feedback.repository.FeedbackRepository
import com.tangem.domain.visa.model.VisaTxDetails
@ -20,37 +20,40 @@ internal class EmailMessageBodyResolver(
/** Resolve email message body by [type] */
suspend fun resolve(type: FeedbackEmailType): String = with(FeedbackDataBuilder()) {
when (type) {
is FeedbackEmailType.DirectUserRequest -> addUserRequestBody(type.cardInfo)
is FeedbackEmailType.RateCanBeBetter -> addCardAndPhoneInfo(type.cardInfo)
is FeedbackEmailType.TransactionSendingProblem -> addTransactionSendingProblemBody(type.cardInfo)
is FeedbackEmailType.DirectUserRequest -> addUserRequestBody(type.walletMetaInfo)
is FeedbackEmailType.RateCanBeBetter -> addCardAndPhoneInfo(type.walletMetaInfo)
is FeedbackEmailType.TransactionSendingProblem -> addTransactionSendingProblemBody(type.walletMetaInfo)
is FeedbackEmailType.StakingProblem -> addStakingProblemBody(type)
is FeedbackEmailType.SwapProblem -> addSwapProblemBody(type)
is FeedbackEmailType.CurrencyDescriptionError -> addTokenInfo(type)
is FeedbackEmailType.PreActivatedWallet -> addUserRequestBody(type.cardInfo)
is FeedbackEmailType.PreActivatedWallet -> addUserRequestBody(type.walletMetaInfo)
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)
is FeedbackEmailType.Visa.Activation -> addUserRequestBody(type.walletMetaInfo)
is FeedbackEmailType.Visa.DirectUserRequest -> addUserRequestBody(type.walletMetaInfo)
is FeedbackEmailType.Visa.Dispute -> addVisaRequestBody(type.walletMetaInfo, type.visaTxDetails)
}
return build()
}
private suspend fun FeedbackDataBuilder.addVisaRequestBody(cardInfo: CardInfo, visaTxDetails: VisaTxDetails) {
addUserRequestBody(cardInfo)
private suspend fun FeedbackDataBuilder.addVisaRequestBody(
walletMetaInfo: WalletMetaInfo,
visaTxDetails: VisaTxDetails,
) {
addUserRequestBody(walletMetaInfo)
addDelimiter()
addVisaTxInfo(visaTxDetails)
}
private suspend fun FeedbackDataBuilder.addUserRequestBody(cardInfo: CardInfo) {
addUserWalletsInfo(userWalletsInfo = feedbackRepository.getUserWalletsInfo(cardInfo.userWalletId))
private suspend fun FeedbackDataBuilder.addUserRequestBody(walletMetaInfo: WalletMetaInfo) {
addUserWalletsInfo(userWalletsInfo = feedbackRepository.getUserWalletsInfo(walletMetaInfo.userWalletId))
addDelimiter()
addCardInfo(cardInfo)
addUserWalletMetaInfo(walletMetaInfo)
addDelimiter()
val userWalletId = cardInfo.userWalletId
val userWalletId = walletMetaInfo.userWalletId
if (userWalletId != null) {
val blockchainInfoList = feedbackRepository.getBlockchainInfoList(userWalletId)
@ -68,11 +71,11 @@ internal class EmailMessageBodyResolver(
addPhoneInfo(phoneInfo = feedbackRepository.getPhoneInfo())
}
private suspend fun FeedbackDataBuilder.addTransactionSendingProblemBody(cardInfo: CardInfo) {
addCardInfo(cardInfo)
private suspend fun FeedbackDataBuilder.addTransactionSendingProblemBody(walletMetaInfo: WalletMetaInfo) {
addUserWalletMetaInfo(walletMetaInfo)
addDelimiter()
val userWalletId = requireNotNull(cardInfo.userWalletId) { "UserWalletId must be not null" }
val userWalletId = requireNotNull(walletMetaInfo.userWalletId) { "UserWalletId must be not null" }
val blockchainError = feedbackRepository.getBlockchainErrorInfo(userWalletId = userWalletId)
val blockchainInfo = blockchainError?.let {
feedbackRepository.getBlockchainInfo(
@ -91,10 +94,10 @@ internal class EmailMessageBodyResolver(
}
private suspend fun FeedbackDataBuilder.addStakingProblemBody(type: FeedbackEmailType.StakingProblem) {
addCardInfo(type.cardInfo)
addUserWalletMetaInfo(type.walletMetaInfo)
addDelimiter()
val userWalletId = requireNotNull(type.cardInfo.userWalletId) { "UserWalletId must be not null" }
val userWalletId = requireNotNull(type.walletMetaInfo.userWalletId) { "UserWalletId must be not null" }
val blockchainError = feedbackRepository.getBlockchainErrorInfo(userWalletId = userWalletId)
val blockchainInfo = blockchainError?.let {
feedbackRepository.getBlockchainInfo(
@ -120,10 +123,10 @@ internal class EmailMessageBodyResolver(
}
private suspend fun FeedbackDataBuilder.addSwapProblemBody(type: FeedbackEmailType.SwapProblem) {
addCardInfo(type.cardInfo)
addUserWalletMetaInfo(type.walletMetaInfo)
addDelimiter()
val userWalletId = requireNotNull(type.cardInfo.userWalletId) { "UserWalletId must be not null" }
val userWalletId = requireNotNull(type.walletMetaInfo.userWalletId) { "UserWalletId must be not null" }
val blockchainError = feedbackRepository.getBlockchainErrorInfo(userWalletId = userWalletId)
val blockchainInfo = blockchainError?.let {
feedbackRepository.getBlockchainInfo(
@ -144,8 +147,8 @@ internal class EmailMessageBodyResolver(
addPhoneInfo(phoneInfo = feedbackRepository.getPhoneInfo())
}
private fun FeedbackDataBuilder.addCardAndPhoneInfo(cardInfo: CardInfo) {
addCardInfo(cardInfo)
private fun FeedbackDataBuilder.addCardAndPhoneInfo(walletMetaInfo: WalletMetaInfo) {
addUserWalletMetaInfo(walletMetaInfo)
addDelimiter()
addPhoneInfo(phoneInfo = feedbackRepository.getPhoneInfo())
}

View file

@ -18,7 +18,7 @@ internal class EmailSubjectResolver(private val resources: Resources) {
fun resolve(type: FeedbackEmailType): String {
return when (type) {
is FeedbackEmailType.DirectUserRequest -> {
if (type.cardInfo.isStart2Coin) {
if (type.walletMetaInfo.isStart2Coin == true) {
resources.getStringSafe(R.string.feedback_subject_support)
} else {
resources.getStringSafe(R.string.feedback_subject_support_tangem)

View file

@ -11,16 +11,15 @@ import com.tangem.core.decompose.navigation.Router
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.domain.card.common.TapWorkarounds.isVisa
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.CardInfo
import com.tangem.domain.feedback.models.WalletMetaInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.feedback.repository.FeedbackFeatureToggles
import com.tangem.domain.redux.LegacyAction
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.walletconnect.CheckIsWalletConnectAvailableUseCase
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.details.component.DetailsComponent
@ -56,7 +55,7 @@ internal class DetailsModel @Inject constructor(
paramsContainer: ParamsContainer,
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
private val appStateHolder: ReduxStateHolder,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val getWalletsUseCase: GetWalletsUseCase,
private val feedbackFeatureToggles: FeedbackFeatureToggles,
@ -121,20 +120,15 @@ internal class DetailsModel @Inject constructor(
val selectedUserWallet = getSelectedWalletSyncUseCase().getOrNull()
?: error("Selected wallet is null")
if (selectedUserWallet is UserWallet.Hot) {
return@launch // TODO [REDACTED_TASK_KEY] [Hot Wallet] Send feedback
}
val scanResponse = selectedUserWallet.requireColdWallet().scanResponse
val cardInfo = getCardInfoUseCase(scanResponse).getOrNull() ?: return@launch
val metaInfo = getWalletMetaInfoUseCase(selectedUserWallet.walletId).getOrNull() ?: return@launch
val feedbackType = when {
userWallets.all { it is UserWallet.Cold && it.scanResponse.card.isVisa } ->
FeedbackEmailType.Visa.DirectUserRequest(cardInfo)
FeedbackEmailType.Visa.DirectUserRequest(metaInfo)
userWallets.all { it !is UserWallet.Cold || it.scanResponse.card.isVisa.not() } ->
FeedbackEmailType.DirectUserRequest(cardInfo)
FeedbackEmailType.DirectUserRequest(metaInfo)
else -> {
showFeedbackEmailTypeOptionBS(cardInfo)
showFeedbackEmailTypeOptionBS(metaInfo)
return@launch
}
}
@ -144,18 +138,14 @@ internal class DetailsModel @Inject constructor(
}
private fun openUseDesk() {
val userWallet = getSelectedWalletSyncUseCase().getOrNull() ?: error("Selected wallet is null")
if (userWallet is UserWallet.Hot) {
return // TODO [REDACTED_TASK_KEY] [Hot Wallet] UseDesk
modelScope.launch {
val userWallet = getSelectedWalletSyncUseCase().getOrNull() ?: error("Selected wallet is null")
val metaInfo = getWalletMetaInfoUseCase.invoke(userWallet.walletId).getOrNull() ?: return@launch
router.push(AppRoute.Usedesk(metaInfo))
}
val cardInfo = getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return
router.push(AppRoute.Usedesk(cardInfo))
}
private fun showFeedbackEmailTypeOptionBS(selectedCardInfo: CardInfo) {
private fun showFeedbackEmailTypeOptionBS(selectedWalletMetaInfo: WalletMetaInfo) {
state.update {
it.copy(
selectFeedbackEmailTypeBSConfig = TangemBottomSheetConfig(
@ -171,7 +161,7 @@ internal class DetailsModel @Inject constructor(
content = SelectEmailFeedbackTypeBS(
onOptionClick = { option ->
onEmailFeedbackTypeOptionSelected(
selectedCardInfo = selectedCardInfo,
selectedWalletMetaInfo = selectedWalletMetaInfo,
option = option,
)
@ -189,32 +179,33 @@ internal class DetailsModel @Inject constructor(
}
private fun onEmailFeedbackTypeOptionSelected(
selectedCardInfo: CardInfo,
selectedWalletMetaInfo: WalletMetaInfo,
option: SelectEmailFeedbackTypeBS.Option,
) {
modelScope.launch {
val feedbackType = when (option) {
SelectEmailFeedbackTypeBS.Option.General -> {
if (selectedCardInfo.isVisa.not()) {
FeedbackEmailType.DirectUserRequest(selectedCardInfo)
if (selectedWalletMetaInfo.isVisa == false) {
FeedbackEmailType.DirectUserRequest(selectedWalletMetaInfo)
} else {
val scanResponse = getWalletsUseCase.invokeSync()
.firstOrNull { it is UserWallet.Cold && it.scanResponse.card.isVisa.not() }
?.requireColdWallet()?.scanResponse ?: return@launch
val userWallet = getWalletsUseCase.invokeSync()
.firstOrNull {
it is UserWallet.Hot || it is UserWallet.Cold && it.scanResponse.card.isVisa.not()
} ?: return@launch
val cardInfo = getCardInfoUseCase(scanResponse).getOrNull() ?: return@launch
FeedbackEmailType.DirectUserRequest(cardInfo)
val metaInfo = getWalletMetaInfoUseCase(userWallet.walletId).getOrNull() ?: return@launch
FeedbackEmailType.DirectUserRequest(metaInfo)
}
}
SelectEmailFeedbackTypeBS.Option.Visa -> {
if (selectedCardInfo.isVisa) {
FeedbackEmailType.Visa.DirectUserRequest(selectedCardInfo)
if (selectedWalletMetaInfo.isVisa == true) {
FeedbackEmailType.Visa.DirectUserRequest(selectedWalletMetaInfo)
} else {
val scanResponse = getWalletsUseCase.invokeSync()
val userWallet = getWalletsUseCase.invokeSync()
.firstOrNull { it is UserWallet.Cold && it.scanResponse.card.isVisa }
?.requireColdWallet()?.scanResponse ?: return@launch
val cardInfo = getCardInfoUseCase(scanResponse).getOrNull() ?: return@launch
FeedbackEmailType.Visa.DirectUserRequest(cardInfo)
?: return@launch
val metaInfo = getWalletMetaInfoUseCase(userWallet.walletId).getOrNull() ?: return@launch
FeedbackEmailType.Visa.DirectUserRequest(metaInfo)
}
}
}

View file

@ -9,7 +9,7 @@ import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.scan.ScanResponse
@ -39,7 +39,7 @@ internal class MultiWalletCreateWalletModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val tangemSdkManager: TangemSdkManager,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val cardRepository: CardRepository,
private val analyticsHandler: AnalyticsEventHandler,
private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory,
@ -168,7 +168,8 @@ internal class MultiWalletCreateWalletModel @Inject constructor(
fun navigateToSupportScreen() {
modelScope.launch {
val cardInfo = getCardInfoUseCase(multiWalletState.value.currentScanResponse).getOrNull() ?: return@launch
val cardInfo =
getWalletMetaInfoUseCase(multiWalletState.value.currentScanResponse).getOrNull() ?: return@launch
sendFeedbackEmailUseCase(FeedbackEmailType.DirectUserRequest(cardInfo))
}
}

View file

@ -9,7 +9,7 @@ import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.scan.CardDTO
@ -48,7 +48,7 @@ internal class MultiWalletFinalizeModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val backupServiceHolder: BackupServiceHolder,
private val tangemSdkManager: TangemSdkManager,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory,
private val userWalletsListManager: UserWalletsListManager,
@ -329,7 +329,8 @@ internal class MultiWalletFinalizeModel @Inject constructor(
private fun navigateToSupportScreen() {
modelScope.launch {
val cardInfo = getCardInfoUseCase(multiWalletState.value.currentScanResponse).getOrNull() ?: return@launch
val cardInfo =
getWalletMetaInfoUseCase(multiWalletState.value.currentScanResponse).getOrNull() ?: return@launch
sendFeedbackEmailUseCase(FeedbackEmailType.DirectUserRequest(cardInfo))
}
}

View file

@ -10,7 +10,7 @@ import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.crypto.bip39.Mnemonic
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.features.hotwallet.MnemonicRepository
@ -44,7 +44,7 @@ internal class MultiWalletSeedPhraseModel @Inject constructor(
private val urlOpener: UrlOpener,
private val tangemSdkManager: TangemSdkManager,
private val cardRepository: CardRepository,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val analyticsHandler: AnalyticsEventHandler,
) : Model() {
@ -257,7 +257,8 @@ internal class MultiWalletSeedPhraseModel @Inject constructor(
fun navigateToSupportScreen() {
modelScope.launch {
val cardInfo = getCardInfoUseCase(multiWalletState.value.currentScanResponse).getOrNull() ?: return@launch
val cardInfo =
getWalletMetaInfoUseCase(multiWalletState.value.currentScanResponse).getOrNull() ?: return@launch
sendFeedbackEmailUseCase(FeedbackEmailType.DirectUserRequest(cardInfo))
}
}

View file

@ -10,7 +10,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.domain.card.common.TapWorkarounds.isVisa
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.features.onboarding.v2.stepper.api.OnboardingStepperComponent
@ -24,7 +24,7 @@ import kotlinx.coroutines.launch
internal class DefaultOnboardingStepperComponent @AssistedInject constructor(
@Assisted val context: AppComponentContext,
@Assisted val params: OnboardingStepperComponent.Params,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val analyticsHandler: AnalyticsEventHandler,
) : OnboardingStepperComponent, AppComponentContext by context {
@ -40,7 +40,7 @@ internal class DefaultOnboardingStepperComponent @AssistedInject constructor(
)
componentScope.launch {
val cardInfo = getCardInfoUseCase(params.scanResponse).getOrNull() ?: return@launch
val cardInfo = getWalletMetaInfoUseCase(params.scanResponse).getOrNull() ?: return@launch
sendFeedbackEmailUseCase(
if (params.scanResponse.card.isVisa) {
FeedbackEmailType.Visa.Activation(cardInfo)

View file

@ -20,14 +20,12 @@ import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.settings.IsSendTapHelpEnabledUseCase
import com.tangem.domain.settings.NeverShowTapHelpUseCase
@ -90,7 +88,7 @@ internal class SendConfirmModel @Inject constructor(
private val createTransferTransactionUseCase: CreateTransferTransactionUseCase,
private val sendTransactionUseCase: SendTransactionUseCase,
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase,
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
@ -340,15 +338,9 @@ internal class SendConfirmModel @Inject constructor(
),
)
if (userWallet is UserWallet.Hot) {
return // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow
}
val cardInfo =
getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return
modelScope.launch {
sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(cardInfo = cardInfo))
val metaInfo = getWalletMetaInfoUseCase.invoke(userWallet.walletId).getOrNull() ?: return@launch
sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(walletMetaInfo = metaInfo))
}
}

View file

@ -18,7 +18,7 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
@ -26,7 +26,6 @@ import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
import com.tangem.domain.qrscanning.usecases.ParseQrCodeUseCase
@ -91,7 +90,7 @@ internal class SendModel @Inject constructor(
private val parseQrCodeUseCase: ParseQrCodeUseCase,
private val sendConfirmAlertFactory: SendConfirmAlertFactory,
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val createTransferTransactionUseCase: CreateTransferTransactionUseCase,
@ -472,15 +471,9 @@ internal class SendModel @Inject constructor(
),
)
if (userWallet is UserWallet.Hot) {
return // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow
}
val cardInfo =
getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return
modelScope.launch {
sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(cardInfo = cardInfo))
val metaInfo = getWalletMetaInfoUseCase(userWallet.walletId).getOrNull() ?: return@launch
sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(walletMetaInfo = metaInfo))
}
}

View file

@ -16,13 +16,11 @@ import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.datasource.local.nft.converter.NFTSdkAssetConverter
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.settings.IsSendTapHelpEnabledUseCase
import com.tangem.domain.settings.NeverShowTapHelpUseCase
import com.tangem.domain.transaction.usecase.CreateNFTTransferTransactionUseCase
@ -78,7 +76,7 @@ internal class NFTSendConfirmModel @Inject constructor(
private val sendTransactionUseCase: SendTransactionUseCase,
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val notificationsUpdateTrigger: SendNotificationsUpdateTrigger,
private val notificationsUpdateListener: SendNotificationsUpdateListener,
@ -249,15 +247,9 @@ internal class NFTSendConfirmModel @Inject constructor(
),
)
if (userWallet is UserWallet.Hot) {
return // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow
}
val cardInfo =
getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return
modelScope.launch {
sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(cardInfo = cardInfo))
val metaInfo = getWalletMetaInfoUseCase(userWallet.walletId).getOrNull() ?: return@launch
sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(walletMetaInfo = metaInfo))
}
}

View file

@ -14,7 +14,7 @@ import com.tangem.datasource.local.nft.converter.NFTSdkAssetConverter
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
@ -22,7 +22,6 @@ import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.tokens.*
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.usecase.CreateNFTTransferTransactionUseCase
@ -71,7 +70,7 @@ internal class NFTSendModel @Inject constructor(
private val createNFTTransferTransactionUseCase: CreateNFTTransferTransactionUseCase,
private val getFeeUseCase: GetFeeUseCase,
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val alertFactory: SendConfirmAlertFactory,
private val sendFeatureToggles: SendFeatureToggles,
@ -224,15 +223,9 @@ internal class NFTSendModel @Inject constructor(
),
)
if (userWallet is UserWallet.Hot) {
return // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow
}
val cardInfo =
getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return
modelScope.launch {
sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(cardInfo = cardInfo))
val metaInfo = getWalletMetaInfoUseCase(userWallet.walletId).getOrNull() ?: return@launch
sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(walletMetaInfo = metaInfo))
}
}

View file

@ -23,7 +23,7 @@ import com.tangem.core.ui.haptic.VibratorHapticManager
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
@ -34,7 +34,6 @@ import com.tangem.domain.models.staking.*
import com.tangem.domain.models.staking.action.StakingActionType
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.staking.*
import com.tangem.domain.staking.analytics.StakeScreenSource
import com.tangem.domain.staking.analytics.StakingAnalyticsEvent
@ -107,7 +106,7 @@ internal class StakingModel @Inject constructor(
private val createApprovalTransactionUseCase: CreateApprovalTransactionUseCase,
private val getAllowanceUseCase: GetAllowanceUseCase,
private val vibratorHapticManager: VibratorHapticManager,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase,
private val getBalanceNotEnoughForFeeWarningUseCase: GetBalanceNotEnoughForFeeWarningUseCase,
private val getCurrencyCheckUseCase: GetCurrencyCheckUseCase,
@ -859,13 +858,8 @@ internal class StakingModel @Inject constructor(
modelScope.launch {
val network = cryptoCurrencyStatus.currency.network
if (userWallet is UserWallet.Hot) {
return@launch // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow
}
val cardInfo =
getCardInfoUseCase(userWallet.requireColdWallet().scanResponse)
.getOrElse { error("CardInfo must be not null") }
val metaInfo =
getWalletMetaInfoUseCase(userWallet.walletId).getOrElse { error("CardInfo must be not null") }
val amountState = uiState.value.amountState as? AmountState.Data
val confirmationState = uiState.value.confirmationState as? StakingStates.ConfirmationState.Data
val validatorState = uiState.value.validatorState as? StakingStates.ValidatorState.Data
@ -887,7 +881,7 @@ internal class StakingModel @Inject constructor(
)
val email = FeedbackEmailType.StakingProblem(
cardInfo = cardInfo,
walletMetaInfo = metaInfo,
validatorName = validator?.name,
transactionTypes = transactionsInProgress.map { it.type.name },
unsignedTransactions = transactionsInProgress.map { it.unsignedTransaction },

View file

@ -8,14 +8,13 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.EventMessageAction
import com.tangem.domain.express.models.ExpressError
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.transaction.error.SendTransactionError
import com.tangem.features.swap.v2.impl.R
import javax.inject.Inject
@ -24,7 +23,7 @@ import javax.inject.Inject
internal class SwapAlertFactory @Inject constructor(
private val uiMessageSender: UiMessageSender,
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
) {
fun getGenericErrorState(expressError: ExpressError, onFailedTxEmailClick: () -> Unit, popBack: () -> Unit = {}) {
@ -96,16 +95,11 @@ internal class SwapAlertFactory @Inject constructor(
),
)
if (userWallet is UserWallet.Hot) {
return // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow
}
val cardInfo =
getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return
val metaInfo = getWalletMetaInfoUseCase(userWallet.walletId).getOrNull() ?: return
sendFeedbackEmailUseCase(
type = FeedbackEmailType.SwapProblem(
cardInfo = cardInfo,
walletMetaInfo = metaInfo,
providerName = confirmData?.quote?.provider?.name.orEmpty(),
txId = txId.orEmpty(),
),

View file

@ -23,7 +23,7 @@ import com.tangem.core.ui.utils.InputNumberFormatter
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
@ -33,7 +33,6 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.promo.ShouldShowStoriesUseCase
import com.tangem.domain.promo.models.StoryContentIds
@ -94,7 +93,7 @@ internal class SwapModel @Inject constructor(
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase,
@ -1364,15 +1363,11 @@ internal class SwapModel @Inject constructor(
),
)
if (userWallet is UserWallet.Hot) {
return@launch // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow
}
val cardInfo = getCardInfoUseCase(userWallet.requireColdWallet().scanResponse)
val metaInfo = getWalletMetaInfoUseCase(userWallet.walletId)
.getOrElse { error("CardInfo must be not null") }
val email = FeedbackEmailType.SwapProblem(
cardInfo = cardInfo,
walletMetaInfo = metaInfo,
providerName = dataState.selectedProvider?.name.orEmpty(),
txId = transaction?.txId.orEmpty(),
)

View file

@ -4,7 +4,7 @@ import arrow.core.getOrElse
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.visa.GetVisaCurrencyUseCase
@ -39,7 +39,7 @@ internal class VisaWalletIntentsImplementor @Inject constructor(
private val eventSender: WalletEventSender,
private val getVisaCurrencyUseCase: GetVisaCurrencyUseCase,
private val getVisaTxDetailsUseCase: GetVisaTxDetailsUseCase,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val getUserWalletsUseCase: GetWalletsUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val dispatchers: CoroutineDispatcherProvider,
@ -101,13 +101,13 @@ internal class VisaWalletIntentsImplementor @Inject constructor(
val userWalletId = stateController.getSelectedWalletId()
val userWallet = getUserWalletsUseCase.invokeSync()
.firstOrNull { it.walletId == userWalletId } ?: return@launch
val cardInfo = getCardInfoUseCase.invoke(
val cardInfo = getWalletMetaInfoUseCase.invoke(
userWallet.requireColdWallet().scanResponse,
).getOrNull() ?: return@launch
sendFeedbackEmailUseCase(
FeedbackEmailType.Visa.Dispute(
cardInfo = cardInfo,
walletMetaInfo = cardInfo,
visaTxDetails = txDetails,
),
)

View file

@ -11,7 +11,7 @@ import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
import com.tangem.domain.card.SetCardWasScannedUseCase
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.currency.CryptoCurrency
@ -107,7 +107,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private val analyticsEventHandler: AnalyticsEventHandler,
private val dispatchers: CoroutineDispatcherProvider,
private val shouldShowPromoWalletUseCase: ShouldShowPromoWalletUseCase,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
private val urlOpener: UrlOpener,
@ -244,15 +244,9 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
neverToSuggestRateAppUseCase()
val userWallet = getSelectedUserWallet() ?: return@launch
val cardInfo = getWalletMetaInfoUseCase(userWallet.walletId).getOrNull() ?: return@launch
if (userWallet is UserWallet.Hot) {
return@launch // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow
}
val scanResponse = userWallet.requireColdWallet().scanResponse
val cardInfo = getCardInfoUseCase(scanResponse).getOrNull() ?: return@launch
sendFeedbackEmailUseCase(type = FeedbackEmailType.RateCanBeBetter(cardInfo = cardInfo))
sendFeedbackEmailUseCase(type = FeedbackEmailType.RateCanBeBetter(walletMetaInfo = cardInfo))
}
}
@ -292,15 +286,9 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
override fun onSupportClick() {
val userWallet = getSelectedUserWallet() ?: return
if (userWallet is UserWallet.Hot) {
return // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow
}
val scanResponse = userWallet.requireColdWallet().scanResponse
val cardInfo = getCardInfoUseCase(scanResponse).getOrNull() ?: return
modelScope.launch {
sendFeedbackEmailUseCase(type = FeedbackEmailType.DirectUserRequest(cardInfo = cardInfo))
val metaInfo = getWalletMetaInfoUseCase(userWallet.walletId).getOrNull() ?: return@launch
sendFeedbackEmailUseCase(type = FeedbackEmailType.DirectUserRequest(walletMetaInfo = metaInfo))
}
}