Updated on 2026-08-14

This commit is contained in:
Tangem 2024-08-21 12:53:58 +05:00
parent 1f8af50d6a
commit 53d63eef85
8 changed files with 106 additions and 4 deletions

View file

@ -86,6 +86,12 @@ internal class FeedbackDataBuilder {
builder.appendKeyValue("Fee", error.fee ?: "Unable to receive")
}
fun addStakingInfo(validatorName: String?, transactionType: String?, unsignedTransaction: String?) {
builder.appendKeyValue("Validator", validatorName ?: "unknown")
builder.appendKeyValue("Action", transactionType ?: "unknown")
builder.appendKeyValue("Unsigned transaction", unsignedTransaction ?: "unknown")
}
fun addDelimiter(): StringBuilder = builder.appendDelimiter()
fun build(): String = builder.trimEnd().toString()

View file

@ -14,7 +14,7 @@ class SaveBlockchainErrorUseCase(
private val feedbackRepository: FeedbackRepository,
) {
fun invoke(error: BlockchainErrorInfo) {
operator fun invoke(error: BlockchainErrorInfo) {
feedbackRepository.saveBlockchainErrorInfo(error = error)
}
}

View file

@ -22,4 +22,12 @@ sealed interface FeedbackEmailType {
/** User has problem with sending transaction */
data class TransactionSendingProblem(override val cardInfo: CardInfo) : FeedbackEmailType
/** User has problem with staking */
data class StakingProblem(
override val cardInfo: CardInfo,
val validatorName: String?,
val transactionType: String?,
val unsignedTransaction: String?,
) : FeedbackEmailType
}

View file

@ -23,6 +23,12 @@ internal class EmailMessageBodyResolver(
is FeedbackEmailType.RateCanBeBetter -> addCardAndPhoneInfo(type.cardInfo)
is FeedbackEmailType.ScanningProblem -> addScanningProblemBody()
is FeedbackEmailType.TransactionSendingProblem -> addTransactionSendingProblemBody(type.cardInfo)
is FeedbackEmailType.StakingProblem -> addStakingProblemBody(
type.cardInfo,
type.validatorName,
type.transactionType,
type.unsignedTransaction,
)
}
return build()
@ -72,6 +78,36 @@ internal class EmailMessageBodyResolver(
addPhoneInfo(phoneInfo = feedbackRepository.getPhoneInfo())
}
private suspend fun FeedbackDataBuilder.addStakingProblemBody(
cardInfo: CardInfo,
validatorName: String?,
transactionType: String?,
unsignedTransaction: String?,
) {
addCardInfo(cardInfo)
addDelimiter()
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,
)
}
if (blockchainInfo != null) {
addBlockchainError(blockchainInfo, blockchainError)
addDelimiter()
}
addStakingInfo(validatorName, transactionType, unsignedTransaction)
addDelimiter()
addPhoneInfo(phoneInfo = feedbackRepository.getPhoneInfo())
}
private fun FeedbackDataBuilder.addCardAndPhoneInfo(cardInfo: CardInfo) {
addCardInfo(cardInfo)
addDelimiter()

View file

@ -19,7 +19,9 @@ internal class EmailMessageTitleResolver(private val resources: Resources) {
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
is FeedbackEmailType.TransactionSendingProblem,
is FeedbackEmailType.StakingProblem,
-> R.string.feedback_preface_tx_failed
}
.let(resources::getString)
}

View file

@ -26,6 +26,7 @@ internal class EmailSubjectResolver(private val resources: Resources) {
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
is FeedbackEmailType.StakingProblem -> R.string.feedback_subject_tx_failed
}
.let(resources::getString)
}

View file

@ -63,6 +63,7 @@ dependencies {
implementation(projects.domain.transaction)
implementation(projects.domain.transaction.models)
implementation(projects.domain.txhistory)
implementation(projects.domain.feedback)
/** Common */
implementation(projects.common.ui)

View file

@ -17,6 +17,11 @@ 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.FeedbackManager
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.staking.*
import com.tangem.domain.staking.model.StakingApproval
import com.tangem.domain.staking.model.stakekit.PendingAction
@ -24,6 +29,7 @@ import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
import com.tangem.domain.staking.model.stakekit.transaction.ActionParams
import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransactionType
import com.tangem.domain.tokens.FetchPendingTransactionsUseCase
import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase
@ -94,6 +100,9 @@ internal class StakingViewModel @Inject constructor(
private val isApproveNeededUseCase: IsApproveNeededUseCase,
private val clipboardManager: ClipboardManager,
private val vibratorHapticManager: VibratorHapticManager,
private val feedbackManager: FeedbackManager,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase,
@DelayedWork private val coroutineScope: CoroutineScope,
savedStateHandle: SavedStateHandle,
) : ViewModel(), DefaultLifecycleObserver, StakingClickIntents {
@ -133,6 +142,8 @@ internal class StakingViewModel @Inject constructor(
private var stakingApproval: StakingApproval = StakingApproval.Empty
private val allowanceTaskScheduler = SingleTaskScheduler<BigDecimal>()
private var transactionInProgress: StakingTransaction? = null
private var approvalJobHolder: JobHolder = JobHolder()
init {
@ -206,9 +217,13 @@ internal class StakingViewModel @Inject constructor(
stakingEventFactory.createStakingErrorAlert(it)
return@launch
}
val gasEstimate = constructedTransaction.gasEstimate
?: return@launch stakingEventFactory.createGenericErrorAlert("Gas estimate is null")
transactionInProgress = constructedTransaction
sendStakingTransaction(
transactionId = constructedTransaction.id,
gasEstimate = constructedTransaction.gasEstimate ?: error("No gas estimate available"),
gasEstimate = gasEstimate,
txData = transactionData,
pendingActionList = confirmationState.pendingActions,
)
@ -503,7 +518,39 @@ internal class StakingViewModel @Inject constructor(
}
override fun onFailedTxEmailClick(errorMessage: String) {
// TODO staking sending feedback email [REDACTED_TASK_KEY]
viewModelScope.launch {
val network = cryptoCurrencyStatus.currency.network
val cardInfo = getCardInfoUseCase(userWallet.scanResponse).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 = confirmationState?.validatorState as? ValidatorState.Content
val feeState = confirmationState?.feeState as? FeeState.Content
val validator = validatorState?.chosenValidator
val feeAmount = feeState?.fee?.amount
val amount = amountState?.amountTextField?.cryptoAmount
saveBlockchainErrorUseCase(
error = BlockchainErrorInfo(
errorMessage = errorMessage,
blockchainId = network.id.value,
derivationPath = network.derivationPath.value,
destinationAddress = validator?.address.orEmpty(),
tokenSymbol = (cryptoCurrencyStatus.currency as? CryptoCurrency.Token)?.symbol,
amount = amount?.run { value?.toPlainString() + currencySymbol }.orEmpty(),
fee = feeAmount?.run { value?.toPlainString() + currencySymbol }.orEmpty(),
),
)
val email = FeedbackEmailType.StakingProblem(
cardInfo = cardInfo,
validatorName = validator?.name,
transactionType = transactionInProgress?.type?.name,
unsignedTransaction = transactionInProgress?.unsignedTransaction,
)
feedbackManager.sendEmail(email)
}
}
fun setRouter(router: InnerStakingRouter, stateRouter: StakingStateRouter) {
@ -601,6 +648,7 @@ internal class StakingViewModel @Inject constructor(
stakingEventFactory.createSendTransactionErrorAlert(error)
},
ifRight = { txHash ->
transactionInProgress = null
submitHash(transactionId, txHash)
scheduleUpdates()
val txUrl = getExplorerTransactionUrlUseCase(