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

@ -18,6 +18,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.feedback.FeedbackManagerFeatureToggles
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetFeedbackEmailUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
@ -112,6 +113,8 @@ interface ApplicationEntryPoint {
fun getDetailsFeatureToggles(): DetailsFeatureToggles
fun getGetCardInfoUseCase(): GetCardInfoUseCase
fun getUrlOpener(): UrlOpener
fun getShareManager(): ShareManager

View file

@ -33,6 +33,7 @@ import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.common.LogConfig
import com.tangem.domain.feedback.FeedbackManagerFeatureToggles
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetFeedbackEmailUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
@ -183,6 +184,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase
get() = entryPoint.getSaveBlockchainErrorUseCase()
private val getCardInfoUseCase: GetCardInfoUseCase
get() = entryPoint.getGetCardInfoUseCase()
private val detailsFeatureToggles: DetailsFeatureToggles
get() = entryPoint.getDetailsFeatureToggles()
@ -284,6 +288,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
settingsRepository = settingsRepository,
blockchainSDKFactory = blockchainSDKFactory,
saveBlockchainErrorUseCase = saveBlockchainErrorUseCase,
getFeedbackEmailUseCase = getFeedbackEmailUseCase,
getCardInfoUseCase = getCardInfoUseCase,
assetLoader = assetLoader,
detailsFeatureToggles = detailsFeatureToggles,
urlOpener = urlOpener,
shareManager = shareManager,

View file

@ -7,13 +7,14 @@ import com.tangem.domain.common.TapWorkarounds
import com.tangem.domain.feedback.FeedbackManagerFeatureToggles
import com.tangem.domain.feedback.GetFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.chat.ChatManager
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.extensions.sendEmail
import com.tangem.tap.common.log.TangemLogCollector
import com.tangem.tap.foregroundActivityObserver
import com.tangem.tap.mainScope
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.withForegroundActivity
import kotlinx.coroutines.launch
@ -36,16 +37,24 @@ class LegacyFeedbackManager(
private var sessionFeedbackFile: File? = null
private var sessionLogsFile: File? = null
fun sendEmail(feedbackData: FeedbackData, onFail: ((Exception) -> Unit)? = null) {
fun sendEmail(feedbackData: FeedbackData, scanResponse: ScanResponse?) {
if (feedbackManagerFeatureToggles.isLocalLogsEnabled) {
mainScope.launch {
scope.launch {
val getCardInfo = suspend {
scanResponse ?: error("ScanResponse must be not null")
store.inject(DaggerGraphState::getCardInfoUseCase).invoke(scanResponse).getOrNull()
?: error("CardInfo must be not null")
}
val email = getFeedbackEmailUseCase(
when (feedbackData) {
is FeedbackEmail -> FeedbackEmailType.DirectUserRequest
is RateCanBeBetterEmail -> FeedbackEmailType.RateCanBeBetter
type = when (feedbackData) {
is FeedbackEmail -> FeedbackEmailType.DirectUserRequest(cardInfo = getCardInfo())
is RateCanBeBetterEmail -> FeedbackEmailType.RateCanBeBetter(cardInfo = getCardInfo())
is ScanFailsEmail -> FeedbackEmailType.ScanningProblem
is SendTransactionFailedEmail -> FeedbackEmailType.TransactionSendingProblem
else -> FeedbackEmailType.DirectUserRequest
is SendTransactionFailedEmail -> {
FeedbackEmailType.TransactionSendingProblem(cardInfo = getCardInfo())
}
else -> FeedbackEmailType.DirectUserRequest(cardInfo = getCardInfo())
},
)
@ -66,12 +75,28 @@ class LegacyFeedbackManager(
subject = activity.getString(feedbackData.subjectResId),
message = feedbackData.joinTogether(activity, infoHolder),
file = getLogFile(activity),
onFail = onFail,
)
}
}
}
fun sendEmail(type: FeedbackEmailType) {
if (!feedbackManagerFeatureToggles.isLocalLogsEnabled) error("LOCAL_LOGS feature toggle must be enabled")
scope.launch {
val email = getFeedbackEmailUseCase(type = type)
store.inject(DaggerGraphState::emailSender).send(
email = EmailSender.Email(
address = email.address,
subject = email.subject,
message = email.message,
attachment = email.file,
),
)
}
}
fun openChat(config: ChatConfig, feedbackData: FeedbackData) {
chatManager.open(
config = config,

View file

@ -1,13 +1,13 @@
package com.tangem.tap.common.feedback
import com.tangem.core.navigation.feedback.FeedbackManager
import com.tangem.core.navigation.feedback.FeedbackType
import com.tangem.domain.feedback.FeedbackManager
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.tap.store
import timber.log.Timber
internal class ProxyFeedbackManager : FeedbackManager {
override fun sendEmail(type: FeedbackType) {
override fun sendEmail(type: FeedbackEmailType) {
val manager = store.state.globalState.feedbackManager
if (manager == null) {
@ -15,14 +15,6 @@ internal class ProxyFeedbackManager : FeedbackManager {
return
}
val data = when (type) {
is FeedbackType.Feedback -> FeedbackEmail()
is FeedbackType.RateCanBeBetter -> RateCanBeBetterEmail()
is FeedbackType.ScanFails -> ScanFailsEmail()
is FeedbackType.SendTransactionFailed -> SendTransactionFailedEmail(type.error)
is FeedbackType.Support -> FeedbackEmail()
}
manager.sendEmail(data)
manager.sendEmail(type)
}
}

View file

@ -81,7 +81,7 @@ sealed class GlobalAction : Action {
data class SetWarningManager(val warningManager: WarningMessagesManager) : GlobalAction()
data class SetFeedbackManager(val feedbackManager: LegacyFeedbackManager) : GlobalAction()
data class SendEmail(val feedbackData: FeedbackData) : GlobalAction()
data class SendEmail(val feedbackData: FeedbackData, val scanResponse: ScanResponse?) : GlobalAction()
data class OpenChat(val feedbackData: FeedbackData, val chatConfig: ChatConfig? = null) : GlobalAction()
object ExchangeManager : GlobalAction() {

View file

@ -69,7 +69,10 @@ private fun handleAction(action: Action, appState: () -> AppState?) {
}
}
is GlobalAction.SendEmail -> {
store.state.globalState.feedbackManager?.sendEmail(action.feedbackData)
store.state.globalState.feedbackManager?.sendEmail(
feedbackData = action.feedbackData,
scanResponse = action.scanResponse,
)
}
is GlobalAction.OpenChat -> {
val globalState = store.state.globalState

View file

@ -1,8 +1,11 @@
package com.tangem.tap.common.redux.legacy
import com.tangem.blockchain.common.AmountType
import com.tangem.domain.feedback.models.BlockchainErrorInfo
import com.tangem.domain.redux.LegacyAction
import com.tangem.domain.tokens.utils.convertToAmount
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.common.feedback.RateCanBeBetterEmail
import com.tangem.tap.common.feedback.SendTransactionFailedEmail
import com.tangem.tap.common.redux.AppState
@ -19,7 +22,10 @@ internal object LegacyMiddleware {
{ action ->
when (action) {
is LegacyAction.SendEmailRateCanBeBetter -> {
store.state.globalState.feedbackManager?.sendEmail(RateCanBeBetterEmail())
store.state.globalState.feedbackManager?.sendEmail(
feedbackData = RateCanBeBetterEmail(),
scanResponse = action.scanResponse,
)
}
is LegacyAction.StartOnboardingProcess -> {
store.dispatch(
@ -28,8 +34,28 @@ internal object LegacyMiddleware {
}
is LegacyAction.SendEmailTransactionFailed -> {
if (store.inject(DaggerGraphState::feedbackManagerFeatureToggles).isLocalLogsEnabled) {
val amount = action.amount?.convertToAmount(action.cryptoCurrency)
store.inject(DaggerGraphState::saveBlockchainErrorUseCase).invoke(
error = BlockchainErrorInfo(
errorMessage = action.errorMessage,
blockchainId = action.cryptoCurrency.network.id.value,
derivationPath = action.cryptoCurrency.network.derivationPath.value,
destinationAddress = action.destinationAddress.orEmpty(),
tokenSymbol = if (amount?.type is AmountType.Token) {
amount.currencySymbol
} else {
""
},
amount = amount?.value?.stripZeroPlainString() ?: "unknown",
fee = action.fee?.convertToAmount(action.cryptoCurrency)
?.value?.stripZeroPlainString() ?: "unknown",
),
)
store.state.globalState.feedbackManager?.sendEmail(
SendTransactionFailedEmail(action.errorMessage),
feedbackData = SendTransactionFailedEmail(action.errorMessage),
scanResponse = action.scanResponse,
)
} else {
scope.launch {
@ -46,7 +72,8 @@ internal object LegacyMiddleware {
)
}
store.state.globalState.feedbackManager?.sendEmail(
SendTransactionFailedEmail(action.errorMessage),
feedbackData = SendTransactionFailedEmail(action.errorMessage),
scanResponse = null,
)
}
}

View file

@ -65,7 +65,7 @@ internal object ScanFailsDialog {
}
customView.findViewById<TextView>(R.id.request_support_button)?.setOnClickListener {
Analytics.send(Basic.ButtonSupport(sourceAnalytics))
store.dispatch(GlobalAction.SendEmail(ScanFailsEmail()))
store.dispatch(GlobalAction.SendEmail(feedbackData = ScanFailsEmail(), scanResponse = null))
}
customView.findViewById<TextView>(R.id.cancel_button)?.setOnClickListener {
store.dispatchDialogHide()

View file

@ -1,11 +1,11 @@
package com.tangem.tap.di
import android.content.Context
import com.tangem.core.navigation.feedback.FeedbackManager
import com.tangem.core.navigation.finisher.AppFinisher
import com.tangem.core.navigation.settings.SettingsManager
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.domain.feedback.FeedbackManager
import com.tangem.tap.common.feedback.ProxyFeedbackManager
import com.tangem.tap.common.finisher.AndroidAppFinisher
import com.tangem.tap.common.settings.IntentSettingsManager

View file

@ -1,6 +1,7 @@
package com.tangem.tap.di.domain
import android.content.Context
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetFeedbackEmailUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.repository.FeedbackRepository
@ -15,6 +16,12 @@ import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
internal object FeedbackDomainModule {
@Provides
@Singleton
fun provideGetCardInfoUseCase(feedbackRepository: FeedbackRepository): GetCardInfoUseCase {
return GetCardInfoUseCase(feedbackRepository = feedbackRepository)
}
@Provides
@Singleton
fun provideGetFeedbackEmailUseCase(

View file

@ -142,7 +142,13 @@ internal class DetailsViewModel(
private fun sendFeedback() {
Analytics.send(Basic.ButtonSupport(AnalyticsParam.ScreensSources.Settings))
store.dispatchOnMain(GlobalAction.SendEmail(FeedbackEmail()))
store.dispatchOnMain(
GlobalAction.SendEmail(
feedbackData = FeedbackEmail(),
scanResponse = userWalletsListManager.selectedUserWalletSync?.scanResponse
?: error("ScanResponse must be not null"),
),
)
}
private fun navigateToAppSettings() {

View file

@ -7,15 +7,20 @@ import androidx.core.view.MenuProvider
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.feedback.SupportInfo
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.store
import com.tangem.utils.Provider
import com.tangem.wallet.R
/**
[REDACTED_AUTHOR]
*/
class OnboardingMenuProvider : MenuProvider {
class OnboardingMenuProvider(
private val scanResponseProvider: Provider<ScanResponse>,
) : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.menu_onboarding, menu)
}
@ -24,7 +29,12 @@ class OnboardingMenuProvider : MenuProvider {
R.id.menu_item_chat_support -> {
Analytics.send(Basic.ButtonSupport(AnalyticsParam.ScreensSources.Intro))
// changed on email support [REDACTED_TASK_KEY]
store.dispatch(GlobalAction.SendEmail(SupportInfo()))
store.dispatch(
GlobalAction.SendEmail(
feedbackData = SupportInfo(),
scanResponse = scanResponseProvider(),
),
)
true
}
else -> false

View file

@ -9,6 +9,8 @@ import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.transitions.HomeToOnboardingTransition
import com.tangem.tap.features.BaseStoreFragment
import com.tangem.tap.features.onboarding.OnboardingMenuProvider
import com.tangem.tap.store
import com.tangem.utils.Provider
import com.tangem.wallet.R
import com.tangem.wallet.databinding.FragmentOnboardingMainBinding
import com.tangem.wallet.databinding.ViewOnboardingProgressBinding
@ -32,7 +34,13 @@ abstract class BaseOnboardingFragment<T> : BaseStoreFragment(R.layout.fragment_o
(activity as? AppCompatActivity)?.setSupportActionBar(binding.toolbar)
}
override fun loadToolbarMenu(): MenuProvider? = OnboardingMenuProvider()
override fun loadToolbarMenu(): MenuProvider = OnboardingMenuProvider(
scanResponseProvider = Provider {
store.state.globalState.onboardingState.onboardingManager?.scanResponse
?: store.state.detailsState.scanResponse
?: error("ScanResponse must be not null")
},
)
protected fun showConfetti(show: Boolean) = with(binding.vConfetti) {
lavConfetti.show(show)

View file

@ -1,7 +1,7 @@
package com.tangem.tap.features.onboarding.products.twins.redux
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.common.getTwinCardNumber
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.tap.common.redux.AppState
import org.rekotlin.Action
@ -35,7 +35,10 @@ private fun internalReduce(action: Action, state: AppState): TwinCardsState {
)
}
is TwinCardsAction.SetStepOfScreen -> {
state = state.copy(currentStep = action.step)
state = state.copy(
currentStep = action.step,
welcomeOnlyScanResponse = (action.step as? TwinCardsStep.WelcomeOnly)?.scanResponse,
)
}
is TwinCardsAction.SetUserUnderstand -> {
state = state.copy(userWasUnderstandIfWalletRecreate = action.isUnderstand)

View file

@ -1,8 +1,8 @@
package com.tangem.tap.features.onboarding.products.twins.redux
import com.tangem.blockchain.common.WalletManager
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.common.TwinCardNumber
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.twins.TwinCardsManager
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
@ -29,6 +29,7 @@ data class TwinCardsState(
val balanceNonCriticalError: TapError? = null,
val balanceCriticalError: TapError? = null,
val showConfetti: Boolean = false,
val welcomeOnlyScanResponse: ScanResponse? = null,
) : StateType {
val steps: List<TwinCardsStep>

View file

@ -6,6 +6,7 @@ import android.view.View
import android.view.animation.OvershootInterpolator
import androidx.annotation.LayoutRes
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.view.MenuProvider
import androidx.core.view.isVisible
import androidx.transition.TransitionManager
import coil.load
@ -25,12 +26,14 @@ import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
import com.tangem.tap.domain.twins.TwinsCardWidget
import com.tangem.tap.features.addBackPressHandler
import com.tangem.tap.features.onboarding.OnboardingMenuProvider
import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep
import com.tangem.tap.store
import com.tangem.utils.Provider
import com.tangem.wallet.R
import com.tangem.wallet.databinding.LayoutOnboardingContainerTopBinding
import dagger.hilt.android.AndroidEntryPoint
@ -61,6 +64,15 @@ internal class OnboardingTwinsFragment : BaseOnboardingFragment<TwinCardsState>(
}
}
override fun loadToolbarMenu(): MenuProvider = OnboardingMenuProvider(
scanResponseProvider = Provider {
store.state.twinCardsState.welcomeOnlyScanResponse
?: store.state.globalState.onboardingState.onboardingManager?.scanResponse
?: store.state.detailsState.scanResponse
?: error("ScanResponse must be not null")
},
)
@Suppress("MagicNumber")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

View file

@ -46,6 +46,7 @@ import com.tangem.tap.features.onboarding.products.wallet.redux.*
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.AccessCodeDialog
import com.tangem.tap.mainScope
import com.tangem.tap.store
import com.tangem.utils.Provider
import com.tangem.wallet.R
import com.tangem.wallet.databinding.FragmentOnboardingWalletBinding
import com.tangem.wallet.databinding.LayoutOnboardingSeedPhraseBinding
@ -115,7 +116,12 @@ class OnboardingWalletFragment :
)
}
override fun loadToolbarMenu(): MenuProvider = OnboardingMenuProvider()
override fun loadToolbarMenu(): MenuProvider = OnboardingMenuProvider(
scanResponseProvider = Provider {
store.state.globalState.onboardingState.onboardingManager?.scanResponse
?: error("ScanResponse must be not null")
},
)
private fun reInitCardsWidgetIfNeeded(backupCardsCounts: Int) = with(binding) {
val viewBackupCount = flCardsContainer.childCount - 1
@ -507,7 +513,13 @@ class OnboardingWalletFragment :
onOpenChat = {
Analytics.send(Basic.ButtonSupport(AnalyticsParam.ScreensSources.Intro))
// changed on email support [REDACTED_TASK_KEY]
store.dispatch(GlobalAction.SendEmail(SupportInfo()))
store.dispatch(
GlobalAction.SendEmail(
feedbackData = SupportInfo(),
scanResponse = store.state.globalState.onboardingState.onboardingManager?.scanResponse
?: error("ScanResponse must be not null"),
),
)
},
onOpenUriClick = { uri ->
store.dispatchOpenUrl(uri.toString())

View file

@ -23,7 +23,13 @@ object WalletActivationErrorDialog {
setNegativeButton(R.string.common_support) { _, _ ->
// changed on email support [REDACTED_TASK_KEY]
Analytics.send(Basic.ButtonSupport(AnalyticsParam.ScreensSources.Intro))
store.dispatch(GlobalAction.SendEmail(SupportInfo()))
store.dispatch(
GlobalAction.SendEmail(
feedbackData = SupportInfo(),
scanResponse = store.state.globalState.onboardingState.onboardingManager?.scanResponse
?: error("ScanResponse must be not null"),
),
)
}
setOnDismissListener { store.dispatchDialogHide() }
setCancelable(false)

View file

@ -7,6 +7,7 @@ import com.tangem.blockchain.common.FeePaidCurrency
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.core.TangemSdkError
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.redux.StateDialog
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.tap.common.analytics.events.Token.Send.AddressEntered
@ -185,12 +186,16 @@ sealed class SendAction : SendScreenAction {
) : Dialog()
sealed class SendTransactionFails : Dialog() {
data class CardSdkError(val error: TangemSdkError) : Dialog()
data class BlockchainSdkError(val error: com.tangem.blockchain.common.BlockchainSdkError) : Dialog()
data class CardSdkError(val error: TangemSdkError, val scanResponse: ScanResponse) : Dialog()
data class BlockchainSdkError(
val error: com.tangem.blockchain.common.BlockchainSdkError,
val scanResponse: ScanResponse,
) : Dialog()
}
data class RequestFeeError(
val error: com.tangem.blockchain.common.BlockchainSdkError,
val scanResponse: ScanResponse,
val onRetry: () -> Unit,
) : Dialog()

View file

@ -1,6 +1,9 @@
package com.tangem.tap.features.send.redux.middlewares
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.blockchain.extensions.Result
import com.tangem.common.extensions.isZero
@ -92,6 +95,7 @@ class RequestFeeMiddleware {
dispatch(
SendAction.Dialog.RequestFeeError(
error = blockchainSdkError,
scanResponse = scanResponse,
onRetry = { dispatch(FeeAction.RequestFee) },
),
)

View file

@ -288,11 +288,25 @@ private fun sendTransaction(
val tangemSdkError = error.tangemError as? TangemSdkError ?: return@withMainContext
if (tangemSdkError is TangemSdkError.UserCancelled) return@withMainContext
dispatch(SendAction.Dialog.SendTransactionFails.CardSdkError(tangemSdkError))
dispatch(
SendAction.Dialog.SendTransactionFails.CardSdkError(
error = tangemSdkError,
scanResponse = store.inject(DaggerGraphState::generalUserWalletsListManager)
.selectedUserWalletSync?.scanResponse
?: error("ScanResponse must be not null"),
),
)
}
is BlockchainSdkError.CreateAccountUnderfunded -> {
// from XLM, XRP, Polkadot
dispatch(SendAction.Dialog.SendTransactionFails.BlockchainSdkError(error))
dispatch(
SendAction.Dialog.SendTransactionFails.BlockchainSdkError(
error = error,
scanResponse = store.inject(DaggerGraphState::generalUserWalletsListManager)
.selectedUserWalletSync?.scanResponse
?: error("ScanResponse must be not null"),
),
)
}
is BlockchainSdkError.Kaspa.UtxoAmountError -> {
dispatch(
@ -331,7 +345,14 @@ private fun sendTransaction(
)
}
else -> {
dispatch(SendAction.Dialog.SendTransactionFails.BlockchainSdkError(error))
dispatch(
SendAction.Dialog.SendTransactionFails.BlockchainSdkError(
error = error,
scanResponse = store.inject(DaggerGraphState::generalUserWalletsListManager)
.selectedUserWalletSync?.scanResponse
?: error("ScanResponse must be not null"),
),
)
}
}
}

View file

@ -23,7 +23,12 @@ object RequestFeeErrorDialog {
setMessage(context.getString(R.string.alert_failed_to_send_transaction_message, errorMessage))
setNegativeButton(R.string.details_row_title_contact_to_support) { _, _ ->
Analytics.send(Basic.ButtonSupport(AnalyticsParam.ScreensSources.Send))
store.dispatch(GlobalAction.SendEmail(SendTransactionFailedEmail(errorMessage)))
store.dispatch(
GlobalAction.SendEmail(
feedbackData = SendTransactionFailedEmail(errorMessage),
scanResponse = dialog.scanResponse,
),
)
}
setPositiveButton(R.string.common_retry) { _, _ -> dialog.onRetry() }
setNeutralButton(R.string.common_cancel) { _, _ -> }

View file

@ -8,6 +8,7 @@ import com.tangem.common.module.ModuleMessageConverter
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.sdk.extensions.localizedDescription
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.common.feedback.SendTransactionFailedEmail
@ -21,21 +22,21 @@ import com.tangem.wallet.R
*/
object SendTransactionFailsDialog {
fun create(context: Context, dialog: SendAction.Dialog.SendTransactionFails.CardSdkError): AlertDialog {
return create(context, dialog.error.localizedDescription(context))
return create(context, dialog.error.localizedDescription(context), dialog.scanResponse)
}
fun create(context: Context, dialog: SendAction.Dialog.SendTransactionFails.BlockchainSdkError): AlertDialog {
val errorConverter = BlockchainSdkErrorConverter(context)
return create(context, errorConverter.convert(dialog.error))
return create(context, errorConverter.convert(dialog.error), dialog.scanResponse)
}
private fun create(context: Context, errorMessage: String): AlertDialog {
private fun create(context: Context, errorMessage: String, scanResponse: ScanResponse): AlertDialog {
return AlertDialog.Builder(context).apply {
setTitle(R.string.alert_failed_to_send_transaction_title)
setMessage(context.getString(R.string.alert_failed_to_send_transaction_message, errorMessage))
setNeutralButton(R.string.details_row_title_contact_to_support) { _, _ ->
Analytics.send(Basic.ButtonSupport(AnalyticsParam.ScreensSources.Send))
store.dispatch(GlobalAction.SendEmail(SendTransactionFailedEmail(errorMessage)))
store.dispatch(GlobalAction.SendEmail(SendTransactionFailedEmail(errorMessage), scanResponse))
}
setPositiveButton(R.string.common_cancel) { _, _ -> }
setOnDismissListener { store.dispatch(SendAction.Dialog.Hide) }

View file

@ -16,6 +16,8 @@ import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.feedback.FeedbackManagerFeatureToggles
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.GetFeedbackEmailUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
@ -82,6 +84,8 @@ data class DaggerGraphState(
val blockchainSDKFactory: BlockchainSDKFactory? = null,
val emailSender: EmailSender? = null,
val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase? = null,
val getFeedbackEmailUseCase: GetFeedbackEmailUseCase? = null,
val getCardInfoUseCase: GetCardInfoUseCase? = null,
val assetLoader: AssetLoader? = null,
val detailsFeatureToggles: DetailsFeatureToggles? = null,
val stakingRouter: StakingRouter? = null,