Skip to content

Commit 9e9a304

Browse files
fix(dashspend): eliminate crash issues when fragments/dialogs recreated and address network error (#1407)
* fix: save merchant info state to prevent crashes when recreating PurchaseGiftCardFragment * fix: don't show PurchaseGiftCardConfirmDialog if there is no merchant info * fix: prevent crash if assetLockPublicKeyId is null * fix: properly handle restoring a merchant from the id * fix: handle SSLHandshakeException * fix: save EnterAmountFragment state * fix: clear EnterAmountFragment state * fix: save state for CoinbaseBuyDashFragment * fix: allow destruction of SearchFragment if nav graph is not on the backstack * fix: allow creation of EnterAmountToTransferFragment if nav graph is not on the backstack * chore: ktlint * chore: remove commented code Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix: pass exception object * fix: remove extra call to setIsFixedDenomination --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1 parent a87db82 commit 9e9a304

File tree

12 files changed

+278
-89
lines changed

12 files changed

+278
-89
lines changed

common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountFragment.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,15 @@ class EnterAmountFragment : Fragment(R.layout.fragment_enter_amount) {
199199
}
200200
}
201201

202+
private fun setAmountFromSavedState() {
203+
if (binding.amountView.dashToFiat) {
204+
binding.amountView.input = viewModel.amount.value?.toPlainString() ?: Coin.ZERO.toPlainString()
205+
} else {
206+
binding.amountView.input = viewModel.fiatAmount.value?.toPlainString()
207+
?: Fiat.valueOf(viewModel.selectedCurrencyCode, 0).toPlainString()
208+
}
209+
}
210+
202211
private fun setupAmountView(dashToFiat: Boolean) {
203212
pickedCurrencyOption = if (dashToFiat) 1 else 0
204213
binding.currencyOptions.setViewCompositionStrategy(
@@ -223,6 +232,7 @@ class EnterAmountFragment : Fragment(R.layout.fragment_enter_amount) {
223232
binding.amountView.dashToFiat = currency.title == Constants.DASH_CURRENCY
224233
}
225234
}
235+
setAmountFromSavedState()
226236

227237
binding.maxButton.setOnClickListener {
228238
lifecycleScope.launch { onMaxAmountButtonClick() }

common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountViewModel.kt

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package org.dash.wallet.common.ui.enter_amount
1919

2020
import androidx.lifecycle.*
21+
import androidx.lifecycle.SavedStateHandle
2122
import dagger.hilt.android.lifecycle.HiltViewModel
2223
import kotlinx.coroutines.ExperimentalCoroutinesApi
2324
import kotlinx.coroutines.flow.*
@@ -34,14 +35,24 @@ import javax.inject.Inject
3435
@OptIn(ExperimentalCoroutinesApi::class)
3536
@HiltViewModel
3637
class EnterAmountViewModel @Inject constructor(
38+
private val savedStateHandle: SavedStateHandle,
3739
val exchangeRates: ExchangeRatesProvider,
3840
private val walletUIConfig: WalletUIConfig
3941
) : ViewModel() {
40-
private val _selectedCurrencyCode = MutableStateFlow(Constants.DEFAULT_EXCHANGE_CURRENCY)
42+
companion object {
43+
private const val KEY_SELECTED_CURRENCY = "selected_currency_code"
44+
private const val KEY_AMOUNT = "amount"
45+
private const val KEY_FIAT_AMOUNT = "fiat_amount"
46+
}
47+
48+
private val _selectedCurrencyCode = MutableStateFlow(
49+
savedStateHandle.get<String>(KEY_SELECTED_CURRENCY) ?: Constants.DEFAULT_EXCHANGE_CURRENCY
50+
)
4151
var selectedCurrencyCode: String
4252
get() = _selectedCurrencyCode.value
4353
set(value) {
4454
_selectedCurrencyCode.value = value
55+
savedStateHandle[KEY_SELECTED_CURRENCY] = value
4556
}
4657

4758
private val _selectedExchangeRate = MutableLiveData<ExchangeRate?>()
@@ -62,11 +73,23 @@ class EnterAmountViewModel @Inject constructor(
6273
val maxAmount: LiveData<Coin>
6374
get() = _maxAmount
6475

65-
internal val _amount = MutableLiveData<Coin>()
76+
internal val _amount = MutableLiveData<Coin>().apply {
77+
savedStateHandle.get<Long>(KEY_AMOUNT)?.let {
78+
value = Coin.valueOf(it)
79+
}
80+
}
6681
val amount: LiveData<Coin>
6782
get() = _amount
6883

69-
internal val _fiatAmount = MutableLiveData<Fiat>()
84+
internal val _fiatAmount = MutableLiveData<Fiat>().apply {
85+
savedStateHandle.get<String>(KEY_FIAT_AMOUNT)?.let { fiatString ->
86+
try {
87+
value = Fiat.parseFiat(selectedCurrencyCode, fiatString)
88+
} catch (_: Exception) {
89+
// Ignore parsing errors for saved state
90+
}
91+
}
92+
}
7093
val fiatAmount: LiveData<Fiat>
7194
get() = _fiatAmount
7295

@@ -112,6 +135,16 @@ class EnterAmountViewModel @Inject constructor(
112135
.filterNotNull()
113136
.onEach { _selectedCurrencyCode.value = it }
114137
.launchIn(viewModelScope)
138+
139+
// Save amount changes to SavedStateHandle
140+
_amount.observeForever { coin ->
141+
savedStateHandle[KEY_AMOUNT] = coin?.value
142+
}
143+
144+
// Save fiat amount changes to SavedStateHandle
145+
_fiatAmount.observeForever { fiat ->
146+
savedStateHandle[KEY_FIAT_AMOUNT] = fiat?.toPlainString()
147+
}
115148
}
116149

117150
fun setMaxAmount(coin: Coin) {
@@ -132,4 +165,12 @@ class EnterAmountViewModel @Inject constructor(
132165
_selectedCurrencyCode.value = walletUIConfig.getExchangeCurrencyCode()
133166
}
134167
}
168+
169+
fun clearSavedState() {
170+
_amount.value = Coin.ZERO
171+
_fiatAmount.value = Fiat.valueOf(selectedCurrencyCode, 0)
172+
savedStateHandle.remove<String>(KEY_SELECTED_CURRENCY)
173+
savedStateHandle.remove<Long>(KEY_AMOUNT)
174+
savedStateHandle.remove<String>(KEY_FIAT_AMOUNT)
175+
}
135176
}

features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/ExploreDataSource.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ interface ExploreDataSource {
8181
suspend fun getMerchantTerritories(): List<String>
8282
suspend fun getAtmTerritories(): List<String>
8383
fun sanitizeQuery(query: String): String
84+
suspend fun getMerchantById(merchantId: String): Merchant?
8485
}
8586

8687
open class MerchantAtmDataSource @Inject constructor(
@@ -509,4 +510,8 @@ open class MerchantAtmDataSource @Inject constructor(
509510
val escapedQuotes = query.replace(Regex.fromLiteral("\""), "\"\"")
510511
return "\"$escapedQuotes*\""
511512
}
513+
514+
override suspend fun getMerchantById(merchantId: String): Merchant? {
515+
return merchantDao.getMerchantById(merchantId)
516+
}
512517
}

features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/MerchantDao.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,4 +489,7 @@ interface MerchantDao : BaseDao<Merchant> {
489489

490490
@Query("SELECT count(*) FROM merchant")
491491
suspend fun getCount(): Int
492+
493+
@Query("SELECT * FROM merchant where merchantId = :merchantId")
494+
suspend fun getMerchantById(merchantId: String): Merchant?
492495
}

features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/CTXSpendRepository.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,14 @@ import org.dash.wallet.features.exploredash.utils.CTXSpendConfig
3434
import java.util.UUID
3535
import java.util.concurrent.TimeUnit
3636
import javax.inject.Inject
37+
import javax.net.ssl.SSLHandshakeException
3738

3839
class CTXSpendException(
3940
message: String,
4041
val errorCode: Int? = null,
41-
val errorBody: String? = null
42-
) : Exception(message) {
42+
val errorBody: String? = null,
43+
cause: Exception? = null
44+
) : Exception(message, cause) {
4345
var resourceString: ResourceString? = null
4446
private val errorMap: Map<String, Any>
4547

@@ -65,6 +67,8 @@ class CTXSpendException(
6567
val fiatAmount = ((errorMap["fields"] as? Map<*, *>)?.get("fiatAmount") as? List<*>)?.firstOrNull()
6668
return errorCode == 400 && (fiatAmount == "above threshold" || fiatAmount == "below threshold")
6769
}
70+
val isNetworkError: Boolean
71+
get() = cause?.let { it is SSLHandshakeException } ?: false
6872
}
6973

7074
class CTXSpendRepository @Inject constructor(

0 commit comments

Comments
 (0)