feat(dashspend): support gift card quantities for piggycards#1483
feat(dashspend): support gift card quantities for piggycards#1483HashEngineering wants to merge 62 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughComposite gift-card support and a V2 PiggyCards purchase flow were added: DB schema and migration for (txId,index), DAOs/repositories/VMs updated for multi-card orders, new Compose UIs/dialogs and numeric entry components, resource/theme updates, test-merchant seeding, build flags, and documentation for EnterAmount and bottom-sheet patterns. ChangesGift card multi-card + PiggyCards V2
Sequence Diagram(s)sequenceDiagram
participant User
participant UI as PurchaseGiftCardFragmentV2
participant VM as DashSpendViewModel
participant Repo as PiggyCardsRepository / CTX
participant DB as Room (GiftCardDao)
User->>UI: select merchant, amounts, quantities
UI->>VM: setGiftCardOrderInfo(fiat, quantity)
UI->>UI: render EnterAmount / DenominationList / NumericKeyboard
UI->>VM: onContinue -> purchaseGiftCard()
VM->>Repo: orderGiftcard(order: GiftCardShoppingCart)
Repo->>Repo: remote API expands order -> List<GiftCardInfo>
Repo->>VM: return List<GiftCardInfo>
VM->>DB: saveGiftCardDummy(txId, List<GiftCardInfo>) (insert rows with index)
VM->>UI: navigate -> GiftCardOrderDetailsDialog (if multiple) or GiftCardDetailsDialog (if single)
UI->>DB: observeCardForTransaction(txId) -> UI updates with list/indices
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 2
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/model/Merchant.kt (1)
80-92:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
equals()/hashCode()contract is broken after addingquantities.
Line 81includesquantitiesin equality, buthashCode()does not. This can cause unstable behavior in hash-based structures.Suggested fix
override fun hashCode(): Int { var result = id.hashCode() result = 31 * result + (name?.hashCode() ?: 0) result = 31 * result + (active?.hashCode() ?: 0) result = 31 * result + denominations.hashCode() result = 31 * result + fixedDenomination.hashCode() result = 31 * result + (savingsPercentage?.hashCode() ?: 0) result = 31 * result + giftCardProviders.map { it.active }.hashCode() + result = 31 * result + quantities.hashCode() return result }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/model/Merchant.kt` around lines 80 - 92, The equals()/hashCode() contract is broken because equals() compares quantities but hashCode() does not; update the Merchant.hashCode() implementation to include quantities (e.g., incorporate quantities.hashCode() into the rolling result using the same 31 * result + ... pattern) so it mirrors equals() which also uses giftCardProviders.map { it.active } and quantities; ensure you update the method referenced as hashCode() in Merchant.kt to keep parity with equals().features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragment.kt (1)
297-339:⚠️ Potential issue | 🟠 Major | ⚡ Quick winUse the full cart total for limit, balance, and discount checks.
These paths still derive the amount from
getFirstCardValueAsFiat(), so aquantity > 1order can show a valid discount and no balance error even when the aggregated cart exceeds the purchase limits or available balance. Please base these checks/texts on the sum ofgiftCardOrderInfo, not the first line item.Also applies to: 406-419
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragment.kt` around lines 297 - 339, The code in PurchaseGiftCardFragment uses viewModel.getFirstCardValueAsFiat() for limit, balance and discount logic which misses quantity > 1 cases; update the checks and displayed amounts to use the aggregated cart total (sum of giftCardOrderInfo) instead of the first line item: replace calls to getFirstCardValueAsFiat() in the block that computes savingsFraction, the zero-value check, exceedsBalance()/showBalanceError(), viewModel.withinLimits(), and the purchaseAmount/discountedAmount used to populate binding.discountValue.text with a cartTotal variable (calculated from giftCardOrderInfo or a new viewModel method that returns the cart total as fiat) so all validations and display use the full order total.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/DashSpendViewModel.kt (1)
248-289:⚠️ Potential issue | 🟠 Major | ⚡ Quick winValidate the selected provider before dereferencing it in the purchase path.
selectedProvider!!andprovider!!turn missing restored state or a missing provider mapping into an NPE right when the user taps continue. This should fail with a domain error instead of crashing.💡 Suggested fix
suspend fun purchaseGiftCard(): List<GiftCardInfo> = withContext(Dispatchers.IO) { _giftCardMerchant.value?.merchantId?.let { ctxSpendConfig.set(CTXSpendConfig.PREFS_LAST_PURCHASE_START, System.currentTimeMillis()) + val resolvedProvider = selectedProvider + ?: getSavedProvider()?.let(GiftCardProviderType::fromProviderName) + ?: throw CTXSpendException("purchaseGiftCard error: no giftcard provider") + val giftCardOrderShoppingCart = GiftCardShoppingCart( giftCardOrderInfo.value.map { GiftCardOrderItem( it.key, it.value ) } ) - val provider = giftCardProviderDao.getProviderByMerchantId(it, selectedProvider!!.name) - when (selectedProvider) { + val provider = giftCardProviderDao.getProviderByMerchantId(it, resolvedProvider.name) + ?: throw CTXSpendException("purchaseGiftCard error: provider mapping missing") + + when (resolvedProvider) { GiftCardProviderType.CTX -> { try { ctxSpendRepository.orderGiftcard( - merchantId = provider!!.sourceId, + merchantId = provider.sourceId, fiatCurrency = Constants.USD_CURRENCY, order = giftCardOrderShoppingCart, cryptoCurrency = Constants.DASH_CURRENCY ) } catch (e: CTXSpendException) { @@ GiftCardProviderType.PiggyCards -> { try { piggyCardsRepository.orderGiftcard( cryptoCurrency = Constants.DASH_CURRENCY, - merchantId = provider!!.sourceId, + merchantId = provider.sourceId, order = giftCardOrderShoppingCart, fiatCurrency = Constants.USD_CURRENCY ) } catch (e: CTXSpendException) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/DashSpendViewModel.kt` around lines 248 - 289, The purchaseGiftCard flow dereferences selectedProvider (!!) and provider (!!) which can cause NPEs; update purchaseGiftCard to validate selectedProvider and the result of giftCardProviderDao.getProviderByMerchantId before using them: if selectedProvider is null or provider is null return/throw a domain-level error (e.g., a specific PurchaseValidationException or CTXSpendException with a clear message) instead of proceeding, and use the validated values in the existing branches (GiftCardProviderType.CTX / PiggyCards) so the subsequent calls (ctxSpendRepository.orderGiftcard, piggyCardsRepository.orderGiftcard) never operate on a null provider or selectedProvider.
🟠 Major comments (19)
common/src/main/java/org/dash/wallet/common/ui/components/TopIntroSend.kt-268-274 (1)
268-274:⚠️ Potential issue | 🟠 Major | ⚡ Quick winIncrease the eye icon tap target for accessibility.
Line 269 limits the interactive area to
20.dp, which is too small for reliable touch interaction.Proposed fix
+import androidx.compose.foundation.layout.sizeIn ... Icon( painter = painterResource( if (isVisible) R.drawable.ic_show else R.drawable.ic_hide ), contentDescription = if (isVisible) { "Hide balance" } else { "Show balance" }, tint = MyTheme.Colors.textSecondary, modifier = Modifier .size(20.dp) + .sizeIn(minWidth = 48.dp, minHeight = 48.dp) .clickable( interactionSource = interactionSource, indication = null, onClick = onToggleClick ) )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@common/src/main/java/org/dash/wallet/common/ui/components/TopIntroSend.kt` around lines 268 - 274, The eye icon's Modifier currently sets a tiny .size(20.dp) for the clickable area; update the Modifier where .size(20.dp) and .clickable(... onToggleClick ...) are used in TopIntroSend.kt so the touch target meets accessibility guidelines (>=48.dp). Replace or augment the modifier with a minimum touch target helper such as .minimumTouchTargetSize() or use .requiredSizeIn(minWidth = 48.dp, minHeight = 48.dp) (while keeping the visible icon at 20.dp) so the clickable area around the eye (and its interactionSource/onToggleClick) is enlarged without changing the visual icon size.common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountCompose.kt-100-113 (1)
100-113:⚠️ Potential issue | 🟠 MajorAdd an accessibility label for the delete button.
The delete key is interactive with both single-click and long-click actions, but currently exposes no readable label to screen readers, which makes correction flow impossible for assistive-tech users.
Suggested patch
import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics ... row.forEach { key -> val isBack = key == "back" + val a11yLabel = if (isBack) "Delete" else key Box( modifier = Modifier .weight(1f) .height(56.dp) + .semantics { role = Role.Button } .background( color = MyTheme.Colors.backgroundSecondary, shape = RoundedCornerShape(10.dp) ... if (isBack) { Icon( painter = painterResource(R.drawable.ic_delete_backward), - contentDescription = null, + contentDescription = a11yLabel, tint = MyTheme.Colors.textPrimary, modifier = Modifier.size(24.dp) )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountCompose.kt` around lines 100 - 113, The delete key (the composable using combinedClickable with onKeyInput and onLongClick when isBack is true, and the Icon with painterResource(R.drawable.ic_delete_backward)) currently has contentDescription = null so screen readers can't identify it; update this composable to provide an accessible label (e.g., contentDescription = if (isBack) "Delete" else null or use stringResource for localization) and ensure the semantics reflect both click and long-click actions (use Modifier.semantics or combinedClickable with role = Role.Button and add onLongClickLabel if available) so assistive tech can announce the button and its long-press behavior while preserving the existing onKeyInput/back_long handling.common/src/main/java/org/dash/wallet/common/ui/dialogs/OffsetDialogFragment.kt-111-114 (1)
111-114:⚠️ Potential issue | 🟠 MajorReplace
AppCompatImageButtoncheck withImageViewto fix non-functional icon assignment.The current type check against
AppCompatImageButtonnever matches any actual layout declarations forcollapse_button. Layout analysis revealscollapse_buttonis declared asImageButton(majority),ImageView(4 layouts), orButton(2 layouts)—neverAppCompatImageButton. This causessetImageResource()to never execute, leaving the icon unset in all scenarios.Using
ImageViewinstead correctly handles all cases:ImageButton(extendsImageView), standaloneImageView, and safely skipsButton(which doesn't supportsetImageResource()).Suggested fix
-import androidx.appcompat.widget.AppCompatImageButton +import android.widget.ImageView ... view.findViewById<View?>(R.id.collapse_button)?.apply { - if (this is AppCompatImageButton) { + if (this is ImageView) { setImageResource(R.drawable.ic_popup_close_circle) } setOnClickListener { dismiss() } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@common/src/main/java/org/dash/wallet/common/ui/dialogs/OffsetDialogFragment.kt` around lines 111 - 114, In OffsetDialogFragment replace the runtime type check that currently tests for AppCompatImageButton on the view found via view.findViewById(R.id.collapse_button) with a check against ImageView so setImageResource(...) actually runs for ImageButton and ImageView variants; specifically, locate the code block in OffsetDialogFragment where collapse_button is retrieved and change the conditional type test to ImageView, then call setImageResource(R.drawable.ic_popup_close_circle) on that ImageView instance so Buttons are skipped and image-bearing views receive the icon.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/dashspend/model/UpdatedMerchantDetails.kt-30-30 (1)
30-30:⚠️ Potential issue | 🟠 MajorAvoid
Doubleas the denomination key for quantity mapping.At Line 30,
Map<Double, Int>creates fragile monetary identity logic. The code directly uses Double denomination values from thedenominationslist to access this map (seen in PurchaseGiftCardFragmentV2.kt and elsewhere), but floating-point precision mismatches will break key equality and return wrong/missing inventory counts.💡 Safer modeling direction
-data class UpdatedMerchantDetails( +data class UpdatedMerchantDetails( val id: String, val denominations: List<Double>, @@ - val quantity: Map<Double, Int> = mapOf() + // key in minor units (e.g., cents) to avoid floating-point key mismatch + val quantityByMinorUnit: Map<Long, Int> = emptyMap() )Convert all denominations to cents (or smallest unit) before map operations. This ensures bit-exact integer matching instead of floating-point approximation.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/dashspend/model/UpdatedMerchantDetails.kt` at line 30, UpdatedMerchantDetails.quantity uses Map<Double, Int>, which causes fragile lookup due to floating-point precision; change the model and all usages to use integer smallest-unit keys (e.g., cents) instead of Double: update the UpdatedMerchantDetails data class field name/type from quantity: Map<Double, Int> to an integer-keyed map (e.g., Map<Long, Int> or Map<Int, Int> representing cents), convert the denominations list values to cents when building or accessing this map, and update all callsites (notably PurchaseGiftCardFragmentV2, any code that reads denominations and indexes quantity) to convert denomination double values to the integer cents key before map lookup and when populating the map to ensure exact equality and avoid floating-point mismatches.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreSyncWorker.kt-173-239 (1)
173-239:⚠️ Potential issue | 🟠 Major | ⚡ Quick winHonor the build flag before touching test-merchant rows.
addPiggyCardsTestMerchantIfNeeded()currently rewrites PiggyCards test data unconditionally because theSUPPORT_PIGGY_CARDS_TEST_MERCHANTgate is commented out. That means production builds still delete/reseed test merchants, and thegetMerchantById()check below is dead code because those rows were just removed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreSyncWorker.kt` around lines 173 - 239, The method addPiggyCardsTestMerchantIfNeeded currently always deletes and re-inserts test merchants because the SUPPORT_PIGGY_CARDS_TEST_MERCHANT gate is commented out; restore and honor that build flag by wrapping the deletion/insert logic (the calls to giftCardProviderDao.deleteByMerchantIds, merchantDao.deleteByMerchantIds, the getMerchantById check, merchantDao.save and giftCardProviderDao.insert loops) inside an if (SUPPORT_PIGGY_CARDS_TEST_MERCHANT) { ... } block (or return early when the flag is false) so production builds skip touching PiggyCardsTestMerchants and the PIGGY_CARDS_TEST_FIXED_MERCHANT_ID existence check remains meaningful.wallet/src/de/schildbach/wallet/service/platform/PlatformSyncService.kt-1244-1247 (1)
1244-1247:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDon't call
first()on an optional gift-card list here.
publishPastTxMetadata()walks every transaction that has metadata, not just gift-card purchases. IfgetCardForTransaction(tx.txId)returns an empty list, this throws and aborts the whole backfill. The same file already treats gift cards as optional ingetUnsavedTransactions()viafirstOrNull(), so this path needs the same guard.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@wallet/src/de/schildbach/wallet/service/platform/PlatformSyncService.kt` around lines 1244 - 1247, publishPastTxMetadata() currently assumes getCardForTransaction(tx.txId) returns a non-empty list and calls giftCard.first(), which throws when the list is empty and aborts the backfill; change this to treat the gift card as optional (like getUnsavedTransactions() uses firstOrNull()), e.g. obtain the gift card with firstOrNull() and only construct TransactionMetadataCacheItem when that gift card is non-null (or handle the null case appropriately), ensuring TransactionMetadataCacheItem(...) is not called with a missing gift card.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/DashSpendUserAuthFragment.kt-243-246 (1)
243-246:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRoute off the verified
provider, notviewModel.selectedProvider.This can turn a successful OTP verification into the generic invalid-code error if
selectedProvideris null/stale after recreation.verifyEmail(provider, code)already proved which provider this flow belongs to, so this branch should use that argument directly and stay exhaustive.Suggested fix
- when (viewModel.selectedProvider) { + when (provider) { GiftCardProviderType.CTX -> safeNavigate(DashSpendUserAuthFragmentDirections.authToPurchaseGiftCardFragment()) GiftCardProviderType.PiggyCards -> safeNavigate(DashSpendUserAuthFragmentDirections.authToPurchaseGiftCardFragmentV2()) - else -> error("serious error. provider = null") }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/DashSpendUserAuthFragment.kt` around lines 243 - 246, The navigation branch is using viewModel.selectedProvider which can be null/stale; instead use the verified provider argument passed into verifyEmail(provider, code) to decide the route. Update the when expression to switch on the local provider parameter (the same one passed into verifyEmail) and handle GiftCardProviderType.CTX -> safeNavigate(DashSpendUserAuthFragmentDirections.authToPurchaseGiftCardFragment()), GiftCardProviderType.PiggyCards -> safeNavigate(DashSpendUserAuthFragmentDirections.authToPurchaseGiftCardFragmentV2()), and an exhaustive else that throws an error if an unknown provider appears; replace references to viewModel.selectedProvider with the provider parameter to avoid stale-null issues.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt-398-402 (1)
398-402:⚠️ Potential issue | 🟠 Major | ⚡ Quick winHandle empty results and missing DASH payment URLs before dereferencing.
data.first()pluspaymentUrls?.get("DASH.DASH")!!will crash on an unexpected provider response instead of surfacing a purchase failure. Please guard both cases and route them into the existing error handling path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt` around lines 398 - 402, Guard against empty or malformed provider responses before dereferencing: check that the incoming data list is not empty and that the first element contains a non-null DASH payment URL (paymentUrls?.get("DASH.DASH") != null) before calling createSendingRequestFromDashUri; if either check fails, route to the existing error path (the same failure handling used elsewhere in this dialog) instead of proceeding to call enterAmountViewModel.clearSavedState(), viewModel.saveGiftCardDummy(transactionId, data) and showGiftCardDetailsDialog(transactionId, data.first().id); update the block around createSendingRequestFromDashUri and the subsequent let { … } to perform these guards and call the dialog’s error handler when guards fail.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsViewModel.kt-357-360 (1)
357-360:⚠️ Potential issue | 🟠 Major | ⚡ Quick winStop polling and surface an error when the PiggyCards order id is missing.
This branch only logs and returns, so the ticker keeps firing and the UI never transitions to a terminal error state. Cancel the ticker and publish an error here instead of retrying forever.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsViewModel.kt` around lines 357 - 360, In GiftCardDetailsViewModel.kt, modify the branch that handles a null orderId (where you call giftCardDao.getCardForTransaction(txid).firstOrNull()?.note and currently only log.error) so that you cancel the active polling ticker (call ticker.cancel() or the cancellation method used where the ticker is created) and publish a terminal error to the UI instead of silently returning (emit/update the ViewModel's error/state flow or LiveData used by this class, e.g., the _viewState/_error LiveData or send a UiEvent) so the UI transitions to an error state and polling stops.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt-488-497 (1)
488-497:⚠️ Potential issue | 🟠 Major | ⚡ Quick winChoose the details dialog by purchased card count, not map-entry count.
giftCardOrderInfo.value.values.sizeis the number of distinct denominations, not the number of cards. A3 x $20purchase still has size1, so this opens the single-card dialog for a multi-card order.data.sizewould be the safest discriminator here.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt` around lines 488 - 497, The condition in showGiftCardDetailsDialog currently uses giftCardOrderInfo.value.values.size (count of distinct denominations) to choose between GiftCardOrderDetailsDialog and GiftCardDetailsDialog; change it to use the actual number of purchased cards instead (e.g. compute totalCards = giftCardOrderInfo.value.values.sumOf { it.size } or use giftCardOrderInfo.value.data.size if a flat list exists) and branch on totalCards > 1 so multi-card purchases open GiftCardOrderDetailsDialog and single-card purchases open GiftCardDetailsDialog.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/CTXSpendRepository.kt-166-170 (1)
166-170:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDon't silently drop extra cart lines in
orderGiftcard().The new signature accepts a shopping cart, but only
order.first()is used. If a multi-entry cart reaches this repository, the request amount no longer matches what the caller thinks it submitted. Either rejectorder.size != 1here or translate every cart line explicitly.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/CTXSpendRepository.kt` around lines 166 - 170, The orderGiftcard method in CTXSpendRepository currently reads only order.first() from the GiftCardShoppingCart and silently ignores additional lines; update orderGiftcard (and its call to purchaseGiftCard) to either validate and reject multi-line carts (throw IllegalArgumentException or return an error when order.size != 1) or iterate the GiftCardShoppingCart and aggregate/translate every cart line into the appropriate purchase requests (e.g., map each entry to a fiatAmount/merchantId pair and call purchaseGiftCard for each or extend purchaseGiftCard to accept multiple lines). Ensure you reference the GiftCardShoppingCart parameter, the orderGiftcard function, and the purchaseGiftCard call when making the change so callers’ submitted amounts are preserved or rejected explicitly.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt-147-149 (1)
147-149:⚠️ Potential issue | 🟠 Major | ⚡ Quick winCompute the confirm-sheet discount per cart line, not from the aggregated total.
getGiftCardDiscount()is denomination-based elsewhere in this flow. Passing the summed order amount here can misstatediscountTextandyouPayTextfor fixed-denomination quantity purchases or mixed-denomination carts. Sum the discounted value per(denomination, qty)instead of applying one fraction to the whole order.Also applies to: 173-188
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt` around lines 147 - 149, Instead of deriving a single savingsFraction from the aggregated orderTotalAmount, compute discounts per cart line: iterate viewModel.giftCardOrderInfo.entries and for each (denomination, qty) call viewModel.getGiftCardDiscount(denomination.toBigDecimal().toDouble()) to get the per-denomination fraction, multiply by denomination * qty to accumulate totalSavings, then compute youPay = orderTotalAmount - totalSavings and build discountText/youPayText from totalSavings and youPay; apply this change where orderTotalAmount/savingsFraction are currently computed (the block using getGiftCardDiscount and the similar logic around lines 173-188) and replace any single-fraction usage with the per-line aggregated savings.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsViewModel.kt-125-140 (1)
125-140:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRebuild
barcodesfrom the latest cards instead of keeping the first snapshot forever.After the first emission, this branch keeps reusing
currentState.barcodes. LaterupdateGiftCardBarcode()writes can update the DAO rows without ever refreshinguiState.barcodes, and newly added card slots won't get a corresponding barcode entry. Re-derive or merge the barcode list on everygiftCardsemission.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsViewModel.kt` around lines 125 - 140, The current .onEach block in the GiftCardDetailsViewModel updates _uiState but only initializes barcodes once (when currentState.barcodes.isEmpty()), causing subsequent giftCards emissions to reuse stale barcode slots; change the update in the .onEach that handles giftCards so barcodes are re-derived or merged on every emission: map the incoming giftCards to Barcode? by checking each giftCard's barcodeValue and barcodeFormat and produce Barcode(value, format) or null, and if you need to preserve user/DAO-updated barcode entries merge them by matching the same identifier (e.g., card id or stable index) against currentState.barcodes so preserved non-null entries are kept, then assign that resulting list to the barcodes field in the _uiState.update (in the same block that currently sets giftCards and index).features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsViewModel.kt-144-150 (1)
144-150:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDon't start a new ticker if one is already active.
This observer launches a fresh
TickerFlowon every qualifying DAO emission and overwritestickerJobwithout cancelling the previous one. A pending order that updates a few times can end up polling the provider multiple times in parallel.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsViewModel.kt` around lines 144 - 150, The current observer can start multiple TickerFlow instances because it always assigns a new tickerJob; modify the block so it first checks tickerJob?.isActive (or tickerJob != null && tickerJob.isActive) and only create/launch the TickerFlow when no active job exists (i.e., skip creating a new one if tickerJob is active). Use the existing symbols: tickerJob, TickerFlow, fetchGiftCardInfo(transactionId) and viewModelScope to locate the code and gate creation accordingly; alternatively, if you prefer restarting behavior, call tickerJob?.cancel() before assigning a new TickerFlow.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardScreenV2.kt-151-158 (1)
151-158:⚠️ Potential issue | 🟠 Major | ⚡ Quick winEnforce
allowedQuantitiesin the stepper.
PurchaseGiftCardV2UiState.allowedQuantitiesnever reachesDenominationList(), so the+button can keep incrementing past the provider's advertised inventory. That makes invalid carts easy to construct and pushes a preventable failure to the backend. Pass the per-denomination cap into the list/row and disable increment at that limit.Also applies to: 160-166, 393-429
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardScreenV2.kt` around lines 151 - 158, The per-denomination cap from PurchaseGiftCardV2UiState.allowedQuantities is never threaded into the UI, so the stepper can increment beyond available inventory; update the call sites (e.g., when handling GiftCardPurchaseMode.FlexibleMultiple in PurchaseGiftCardScreenV2 and the other similar blocks at 160-166 and 393-429) to pass the matching allowedQuantities list into FlexibleMultipleContent (and any downstream composables like DenominationList/DenominationRow or the stepper component), then update the stepper logic to disable the "+" action (and prevent onQuantityChanged from increasing) when the current quantity >= allowedQuantities[index] for that denomination. Ensure indices align between denominations and allowedQuantities and propagate the allowed cap through the same props the current quantity uses.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragmentV2.kt-134-181 (1)
134-181:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAlign the balance check with the insufficient-funds error state.
canContinuerejectsamount == balanceMax/totalDouble == balanceMaxbecause it uses<, but the error branch only reports insufficient funds for> balanceMax. At the exact balance, the CTA is disabled with no explanation.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragmentV2.kt` around lines 134 - 181, The CTA is disabled at exactly-equal balance because canContinue uses strict '<' against balanceMax while errorText only treats '>' as insufficient funds; update both the eligibility and error checks to treat equality consistently. In the GiftCardPurchaseFragmentV2 logic, change the FlexibleSingle canContinue check (variable canContinue / branch GiftCardPurchaseMode.FlexibleSingle) from amount < balanceMax to amount <= balanceMax and adjust the matching errorText branch to mark amount >= balanceMax as insufficient funds; likewise update the FlexibleMultiple/Fixed branch (where totalDouble is compared to fiatBalance.toBigDecimal().toDouble()) from totalDouble < balanceMax to totalDouble <= balanceMax and make the error branch use totalDouble >= balanceMax so CTA state and error messaging align.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/DashSpendViewModel.kt-190-197 (1)
190-197: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick winKeep the new ViewModel state immutable to callers.
These new properties are public
MutableStateFlows, and the fragment already mutates them directly. That makes it easy to bypass purchase/cart invariants as this flow grows; keep the mutable backing state private and expose immutableStateFlows instead.As per coding guidelines, "Use private mutable
_uiStatewith public immutableuiStateviaasStateFlow()in ViewModels".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/DashSpendViewModel.kt` around lines 190 - 197, Make the mutable flows private and expose immutable StateFlow views: rename the public MutableStateFlow symbols to private backing properties (e.g., _isFixedDenominationMultiple: MutableStateFlow<Boolean?> and _giftCardOrderInfo: MutableStateFlow<Map<Double,Int>>) and add public read-only properties isFixedDenominationMultiple: StateFlow<Boolean?> = _isFixedDenominationMultiple.asStateFlow() and giftCardOrderInfo: StateFlow<Map<Double,Int>> = _giftCardOrderInfo.asStateFlow(); update any callers to mutate only the private backing properties (or via ViewModel methods) instead of directly modifying the previously-public MutableStateFlow instances.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/PiggyCardsRepository.kt-381-387 (1)
381-387:⚠️ Potential issue | 🟠 Major | ⚡ Quick winApply the option-card
productIdto every cart item.In the option-card path, only
thisOrder.first()gets aproductId. Any additional cart entries keep0, so the order payload becomes invalid as soon as the cart contains more than one item.💡 Suggested fix
} else if (optionGiftcard != null) { - // there is probably a bug here, but there are no option cards to test - thisOrder.first().productId = optionGiftcard.id + thisOrder.forEach { orderItem -> + orderItem.productId = optionGiftcard.id + } } else {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/PiggyCardsRepository.kt` around lines 381 - 387, The option-card branch only sets productId on thisOrder.first() leaving other OrderItem.productId values unset; change the else-if handling so that when optionGiftcard != null you iterate over thisOrder (same as the rangeGiftCard branch) and assign orderItem.productId = optionGiftcard.id for each item (use the existing thisOrder.forEach lambda and optionGiftcard.id) so every cart item gets the correct productId.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/GiftCardDao.kt-41-55 (1)
41-55:⚠️ Potential issue | 🟠 Major | ⚡ Quick winOrder multi-card queries by
index.These queries now return multiple rows per
txId, but they don't specifyORDER BY \index`. The rest of this flow uses positionalcardIndex`, so an unstable row order can show or update the wrong card/barcode for a transaction.💡 Suggested fix
- `@Query`("SELECT * FROM gift_cards WHERE txId = :txId") + `@Query`("SELECT * FROM gift_cards WHERE txId = :txId ORDER BY `index` ASC") suspend fun getCardForTransaction(txId: Sha256Hash): List<GiftCard> - `@Query`("SELECT * FROM gift_cards WHERE txId = :txId") + `@Query`("SELECT * FROM gift_cards WHERE txId = :txId ORDER BY `index` ASC") fun observeCardForTransaction(txId: Sha256Hash): Flow<List<GiftCard>> `@MapInfo`(keyColumn = "txId") - `@Query`("SELECT * FROM gift_cards") + `@Query`("SELECT * FROM gift_cards ORDER BY txId, `index` ASC") fun observeGiftCards(): Flow<Map<Sha256Hash, List<GiftCard>>>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/GiftCardDao.kt` around lines 41 - 55, Queries that return multiple GiftCard rows must have a stable order by index to ensure positional cardIndex matches rows; update the `@Query` for getCardForTransaction and observeCardForTransaction to include "ORDER BY `index`" so the list is ordered by card index, and update the `@Query` used by observeGiftCards (with `@MapInfo` keyColumn = "txId") to order by txId and `index` (e.g. "ORDER BY txId, `index`") so each Map value List<GiftCard> preserves correct per-transaction ordering.
🟡 Minor comments (6)
common/src/main/java/org/dash/wallet/common/ui/components/TopIntroSend.kt-262-266 (1)
262-266:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winLocalize accessibility labels instead of hardcoded English.
Lines 262-266 use hardcoded content descriptions (
"Hide balance"/"Show balance"), which won’t be translated.Proposed fix
contentDescription = if (isVisible) { - "Hide balance" + stringResource(R.string.hide_balance) } else { - "Show balance" + stringResource(R.string.show_balance) },Also add resources:
<string name="hide_balance">Hide balance</string> <string name="show_balance">Show balance</string>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@common/src/main/java/org/dash/wallet/common/ui/components/TopIntroSend.kt` around lines 262 - 266, The contentDescription in TopIntroSend.kt is using hardcoded English ("Hide balance"/"Show balance") which prevents localization; update the conditional to use string resources via stringResource(R.string.hide_balance) and stringResource(R.string.show_balance) for the contentDescription (locate the contentDescription property in the TopIntroSend composable), and add corresponding entries in strings.xml (<string name="hide_balance">Hide balance</string> and <string name="show_balance">Show balance</string>) so the labels can be translated..claude/agents/DEVELOPMENT-PATTERNS.md-1326-1326 (1)
1326-1326:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd a language identifier to the fenced code block.
Line [1326] uses a plain triple-backtick fence; markdownlint MD040 expects a language tag.
🧩 Suggested lint fix
-``` +```kotlin `@AndroidEntryPoint` class FeatureDetailsDialog : ComposeBottomSheet() { // Layer 1: lifecycle + plumbing override fun Content() { FeatureDetailsContent(...) } // bridges to layer 2 } @@ -``` +```🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.claude/agents/DEVELOPMENT-PATTERNS.md at line 1326, The fenced code block containing the Kotlin snippet for the FeatureDetailsDialog class (references: FeatureDetailsDialog, ComposeBottomSheet, Content, FeatureDetailsContent) is missing a language identifier; update the opening triple-backtick fence to include "kotlin" (i.e., replace ``` with ```kotlin) so markdownlint MD040 is satisfied and the code block is properly highlighted.features/exploredash/src/main/res/values/strings-explore-dash.xml-229-229 (1)
229-229:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUse a plural resource for inventory errors instead of hardcoded “cards”.
Line 229 always renders “cards”, so quantity
1will be grammatically wrong and harder to localize.💡 Proposed i18n-safe change
- <string name="purchase_gift_card_insufficient_inventory">Insufficient inventory for %s to buy %d x %s cards</string> + <plurals name="purchase_gift_card_insufficient_inventory"> + <item quantity="one">Insufficient inventory for %1$s to buy %2$d x %3$s card</item> + <item quantity="other">Insufficient inventory for %1$s to buy %2$d x %3$s cards</item> + </plurals>Then resolve with
resources.getQuantityString(..., quantity, merchant, quantity, denomination).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/res/values/strings-explore-dash.xml` at line 229, Replace the hardcoded singular/plural string "purchase_gift_card_insufficient_inventory" with an Android plurals resource (e.g., name it purchase_gift_card_insufficient_inventory) and update the call site to use resources.getQuantityString(pluralsId, quantity, merchant, quantity, denomination) so the message correctly pluralizes "card(s)" based on the quantity; locate usages referencing the string resource name purchase_gift_card_insufficient_inventory and change them to request the plurals resource with the quantity and the existing parameters (merchant, quantity, denomination).common/src/main/java/org/dash/wallet/common/ui/components/TopNavBase.kt-228-244 (1)
228-244:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix incorrect KDoc: function has both back and close, not "back chevron only".
The documentation was copied from
NavBarBackand doesn't match the actual functionality.📝 Proposed fix
-/** NavBarBack — back chevron only, no title. */ +/** NavBarBackClose — back chevron + close button, no title. */ `@Composable` fun NavBarBackClose(🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@common/src/main/java/org/dash/wallet/common/ui/components/TopNavBase.kt` around lines 228 - 244, Update the KDoc above the NavBarBackClose composable to accurately describe that this function provides both a back chevron (leading) and a close button (trailing), instead of the current "back chevron only" text; locate the comment for the NavBarBackClose function and replace the copied NavBarBack description with a concise summary mentioning both back and close actions and their callbacks (onBackClick, onCloseClick).features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsDialog.kt-222-226 (1)
222-226:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winKeep the logged retry count in sync with
WAIT_LIMIT_FOR_ERROR.
WAIT_LIMIT_FOR_ERRORis60, but this message says "after 10 tries". That makes the support/analytics breadcrumb inaccurate on the only path where it matters.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsDialog.kt` around lines 222 - 226, The error breadcrumb message in the LaunchedEffect block (inside GiftCardDetailsDialog.kt) is hardcoding "after 10 tries" while the actual threshold is WAIT_LIMIT_FOR_ERROR / waitLimitForError; update the onErrorLogged call inside the LaunchedEffect (the block that checks uiState.error and uiState.queries == waitLimitForError) to interpolate the actual constant/variable (WAIT_LIMIT_FOR_ERROR or waitLimitForError) instead of the literal 10 so the logged message for uiState.giftCard?.merchantName reflects the real retry count.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragmentV2.kt-177-190 (1)
177-190:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winBuild the inventory error from the denomination that actually exceeds stock.
You already compute
denomsExceedInventory, but the message is formatted fromdenominationQuantities.entries.first(). If the first selected denomination is still in stock, the user gets the wrong amount/quantity in the error.💡 Suggested fix
- val firstCard = denominationQuantities.entries.first() + val firstCard = denomsExceedInventory.first() getString( R.string.purchase_gift_card_insufficient_inventory, merchant?.name, firstCard.value, Fiat.parseFiat("$", firstCard.key.toString()).toFormattedString() )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragmentV2.kt` around lines 177 - 190, The inventory error uses denominationQuantities.entries.first() instead of the actual out-of-stock entry; change the code to select the first element from denomsExceedInventory (e.g., val firstCard = denomsExceedInventory.first()) and then format the message using that entry's key/ value and merchant?.name so the error shows the denomination and quantity that truly exceed inventory (refer to denomsExceedInventory, denominationQuantities, and firstCard in PurchaseGiftCardFragmentV2).
🧹 Nitpick comments (3)
features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/DashSpendRepository.kt (1)
23-43: 🏗️ Heavy liftAvoid repository dependency on a UI-layer model.
Line [23] imports
GiftCardShoppingCartfromui.dashspend, which couples the repository contract to UI types. Please move this cart model (or a dedicated order DTO) into a domain/data package and have UI map into it.🏗️ Directional boundary fix (example)
-import org.dash.wallet.features.exploredash.ui.dashspend.GiftCardShoppingCart +import org.dash.wallet.features.exploredash.data.dashspend.model.GiftCardOrder interface DashSpendRepository { @@ - suspend fun orderGiftcard( + suspend fun orderGiftcard( cryptoCurrency: String, fiatCurrency: String, - order: GiftCardShoppingCart, + order: GiftCardOrder, merchantId: String, ): List<GiftCardInfo>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/DashSpendRepository.kt` around lines 23 - 43, DashSpendRepository currently depends on a UI model (GiftCardShoppingCart imported from ui.dashspend) which violates layer boundaries; move or recreate a domain-level order DTO (e.g., GiftCardOrder or GiftCardRequest) into a domain/data package and update the DashSpendRepository.orderGiftcard signature to accept that DTO instead of GiftCardShoppingCart, then update UI code to map its GiftCardShoppingCart to the new DTO before calling signup/login/ orderGiftcard; ensure all references to GiftCardShoppingCart in DashSpendRepository and related repository implementations are replaced with the new domain DTO.common/src/main/java/org/dash/wallet/common/ui/components/Grapper.kt (1)
54-58: ⚡ Quick winFix naming typo to match the component name.
On Line [56],
GrapperPreviewshould beGrabberPreview. Also consider renamingGrapper.kttoGrabber.ktso file and symbol names align.✏️ Suggested rename in this file
-@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) -@Composable -private fun GrapperPreview() { +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +private fun GrabberPreview() { Grabber() }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@common/src/main/java/org/dash/wallet/common/ui/components/Grapper.kt` around lines 54 - 58, Rename the preview function GrapperPreview to GrabberPreview and update any references so it calls the existing composable Grabber(), and also rename the file Grapper.kt to Grabber.kt to keep filename and symbol names consistent; ensure the Kotlin file name and the `@Composable` function name match (GrabberPreview) and update any imports/usages that reference GrapperPreview or Grapper.kt.common/src/main/java/org/dash/wallet/common/data/entity/GiftCard.kt (1)
21-21: 💤 Low valueUnused import:
PrimaryKeyis no longer used.The primary key is now defined in the
@Entityannotation. This import can be removed.🧹 Proposed fix
import androidx.room.Entity -import androidx.room.PrimaryKey import com.google.zxing.BarcodeFormat🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@common/src/main/java/org/dash/wallet/common/data/entity/GiftCard.kt` at line 21, The import androidx.room.PrimaryKey is unused in the GiftCard class because the primary key is declared in the `@Entity` annotation; remove the unused import statement to clean up the file (look for the import line referencing PrimaryKey in the GiftCard.kt file and delete it).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ffa3d319-cf48-442d-a712-7c193c2877ef
📒 Files selected for processing (62)
.claude/agents/DEVELOPMENT-PATTERNS.mdcommon/build.gradlecommon/src/main/java/org/dash/wallet/common/data/entity/GiftCard.ktcommon/src/main/java/org/dash/wallet/common/services/TransactionMetadataProvider.ktcommon/src/main/java/org/dash/wallet/common/ui/components/ButtonLarge.ktcommon/src/main/java/org/dash/wallet/common/ui/components/ButtonStyle.ktcommon/src/main/java/org/dash/wallet/common/ui/components/DashList.ktcommon/src/main/java/org/dash/wallet/common/ui/components/Grapper.ktcommon/src/main/java/org/dash/wallet/common/ui/components/ListItem.ktcommon/src/main/java/org/dash/wallet/common/ui/components/MyImages.ktcommon/src/main/java/org/dash/wallet/common/ui/components/MyTheme.ktcommon/src/main/java/org/dash/wallet/common/ui/components/TopIntroSend.ktcommon/src/main/java/org/dash/wallet/common/ui/components/TopNavBase.ktcommon/src/main/java/org/dash/wallet/common/ui/dialogs/ComposeBottomSheet.ktcommon/src/main/java/org/dash/wallet/common/ui/dialogs/OffsetDialogFragment.ktcommon/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountCompose.ktcommon/src/main/java/org/dash/wallet/common/util/MonetaryExt.ktcommon/src/main/res/drawable/ic_dash_d_gray.xmlcommon/src/main/res/drawable/ic_nav_bar_info.xmlcommon/src/main/res/layout/dialog_compose_sheet.xmlcommon/src/main/res/values/strings.xmlfastlane/Fastfilefeatures/exploredash/build.gradlefeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreSyncWorker.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/dashspend/GiftCardProviderDao.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/dashspend/model/UpdatedMerchantDetails.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/GiftCardDao.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/MerchantDao.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/model/Merchant.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/CTXSpendRepository.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/DashSpendRepository.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/PiggyCardsRepository.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/DashSpendUserAuthFragment.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/DashSpendViewModel.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/MerchantDenominations.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragment.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragmentV2.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardScreenV2.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsDialog.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsViewModel.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardOrderDetailsDialog.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardOrderDetailsViewModel.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardViewModel.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/explore/SearchFragment.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/PiggyCardsConstants.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/PiggyCardsTestMerchantData.ktfeatures/exploredash/src/main/res/drawable/ic_gift_card_icon.xmlfeatures/exploredash/src/main/res/layout/dialog_confirm_purchase_gift_card.xmlfeatures/exploredash/src/main/res/layout/dialog_gift_card_details.xmlfeatures/exploredash/src/main/res/navigation/nav_explore.xmlfeatures/exploredash/src/main/res/values/strings-explore-dash.xmlwallet/build.gradlewallet/schemas/de.schildbach.wallet.database.AppDatabase/18.jsonwallet/src/de/schildbach/wallet/database/AppDatabase.ktwallet/src/de/schildbach/wallet/database/AppDatabaseMigrations.ktwallet/src/de/schildbach/wallet/di/DatabaseModule.ktwallet/src/de/schildbach/wallet/service/WalletTransactionMetadataProvider.ktwallet/src/de/schildbach/wallet/service/platform/PlatformSyncService.ktwallet/src/de/schildbach/wallet/ui/OnboardingActivity.ktwallet/src/de/schildbach/wallet/ui/TransactionResultViewModel.ktwallet/src/de/schildbach/wallet/ui/main/WalletTransactionsFragment.kt
💤 Files with no reviewable changes (3)
- features/exploredash/src/main/res/layout/dialog_confirm_purchase_gift_card.xml
- common/src/main/java/org/dash/wallet/common/ui/components/ButtonLarge.kt
- common/src/main/java/org/dash/wallet/common/ui/components/ButtonStyle.kt
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (5)
features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/PiggyCardsRepository.kt (1)
248-335: 🏗️ Heavy liftHeavy duplication with the existing
getMerchant(merchantId).This new overload re-implements ~80 lines that already exist in the original
getMerchant(merchantId)at lines 156–246 (brand lookup, disabled-card filtering, immediate/non-immediate split, option/range card handling, fallbackUpdatedMerchantDetails). The only material difference is the priority tree being pruned perDenominationType. Any future fix (e.g., disabled-merchant rules, discount math,quantitymap) now has to be made in two places and will drift.Consider consolidating: extract the brand+gift-card resolution and each card-shape branch (fixed / option / range / empty) into private helpers, and have both public entry points call them with a flag/type. At minimum, have the original
getMerchant(merchantId)delegate to this overload (e.g., tryingFixedthenMinMax) so the empty / disabled / discount logic lives in one spot.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/PiggyCardsRepository.kt` around lines 248 - 335, The two getMerchant overloads duplicate brand lookup, disabled-card filtering and card-shape handling; refactor by extracting helpers such as resolveBrandAndGiftCards(merchantId): Pair<Brand, List<GiftCard>> and handlers buildFixedMerchantDetails(...), buildOptionMerchantDetails(...), buildRangeMerchantDetails(... ) that encapsulate the common logic (use symbols giftCardMap, disabledGiftCards, disabledMerchants, SERVICE_FEE, UpdatedMerchantDetails), then have getMerchant(merchantId, type: DenominationType) call those helpers according to DenominationType.Fixed / MinMax and make the original getMerchant(merchantId) delegate to the overload (e.g., try Fixed then MinMax) so all filtering, discount math and enabled/productId logic live in one place.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt (2)
491-507: 💤 Low value
giftCardIdparameter is unused.
showGiftCardDetailsDialogacceptsgiftCardId: Stringbut never reads it (both branches only usetxId). Either drop the parameter from the signature and the call site on Line 405, or pass it on toGiftCardOrderDetailsDialog.newInstance/GiftCardDetailsDialog.newInstanceif the index-aware flow needs it.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt` around lines 491 - 507, The function showGiftCardDetailsDialog(txId: Sha256Hash, giftCardId: String) declares giftCardId but never uses it; remove the unused parameter from the signature and update its callers (e.g., the call site noted in the review) to stop passing giftCardId, OR if the dialogs require the giftCardId for index-aware behavior, pass giftCardId into GiftCardOrderDetailsDialog.newInstance(...) and GiftCardDetailsDialog.newInstance(...) and adjust those constructors/factory methods accordingly (update newInstance signatures and any Dialog classes that consume it).
564-571: 💤 Low valueDrop the commented-out alternate Text block.
These eight commented-out lines duplicate what
EnterAmountnow renders. Leaving them in invites drift if the design evolves. Either delete or move to a code review note.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt` around lines 564 - 571, Remove the commented-out alternate Text block inside PurchaseGiftCardConfirmDialog (the eight commented lines that render uiState.purchaseValueText) because EnterAmount now provides that UI; delete those commented lines so the file only uses EnterAmount and avoid duplicated/obsolete code, keeping the dialog implementation clean and preventing drift.features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragmentV2.kt (2)
254-280: 💤 Low valueRemove the commented-out total/quantity blocks.
Lines 255-260 and 274-279 each preserve an earlier version of the order-info update logic that's now centralized elsewhere (the
setGiftCardOrderInfocall inonContinueforFlexibleSingle, and the directgiftCardOrderInfo.valuewrites for the multi-card paths). Leaving both copies risks someone re-enabling the dead version during a merge conflict. Drop them and rely on git history if needed.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragmentV2.kt` around lines 254 - 280, Remove the dead/commented blocks that compute total/qty from denominationQuantities and the earlier viewModel updates: in PurchaseGiftCardFragmentV2 (inside the onContinue lambda handling mode), delete the commented sections around lines where denominationQuantities.entries.sumOf { (d, q) -> d * q } and denominationQuantities.values.sum() are preserved so the code relies only on the existing FlexibleSingle path that parses amountText and calls viewModel.setGiftCardOrderInfo(fiat, 1) and the existing multi-card/fixed paths that set giftCardOrderInfo elsewhere; leave setGiftCardOrderInfo, amountText, and giftCardOrderInfo references intact and rely on git history if the old logic is needed.
144-196: ⚡ Quick winHoist the
2500cap into a named constant.
2500appears three times — twice as the gating value (Line 148 and Line 182) and once as the message argument (Line 183). If the cap ever changes, it's easy to miss one of these sites and ship a UI that says "$2,500" while actually accepting a different limit. Centralizing also makes the rule self-documenting.♻️ Proposed refactor
companion object { private val log = LoggerFactory.getLogger(PurchaseGiftCardFragmentV2::class.java) + private const val MAX_MULTIPLE_PURCHASE_USD = 2500.0 } @@ - totalDouble >= min && totalDouble < fiatBalance.toBigDecimal().toDouble() && !isBlockchainReplaying && totalDouble < 2500 && !exceedsInventory + totalDouble >= min && + totalDouble < fiatBalance.toBigDecimal().toDouble() && + !isBlockchainReplaying && + totalDouble < MAX_MULTIPLE_PURCHASE_USD && + !exceedsInventory @@ - } else if (totalDouble > 2500.0) { - getString(R.string.purchase_gift_card_max_multiple_error, Fiat.parseFiat(Constants.USD_CURRENCY, 2500.00.toString()).toFormattedString()) + } else if (totalDouble > MAX_MULTIPLE_PURCHASE_USD) { + getString( + R.string.purchase_gift_card_max_multiple_error, + Fiat.parseFiat(Constants.USD_CURRENCY, MAX_MULTIPLE_PURCHASE_USD.toString()) + .toFormattedString() + )Same comment applies to the duplicate cap inside
PurchaseGiftCardConfirmDialogif/when it lands there.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragmentV2.kt` around lines 144 - 196, Hoist the hard-coded 2500 into a named constant (e.g. GIFT_CARD_PURCHASE_CAP_USD) and replace all literal occurrences in this fragment: the gating check using totalDouble < 2500 (in the flexibleMultiple branch), the error branch totalDouble > 2500.0, and the Fiat.parseFiat call that builds the max message (used as the message argument). Define the constant in a clear scope (a companion object in PurchaseGiftCardFragmentV2 or a shared Constants file) and reference it where the code currently uses 2500/2500.0/2500.00.toString(); also apply the same constant to PurchaseGiftCardConfirmDialog when it’s updated to keep behavior and messaging consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.claude/agents/DEVELOPMENT-PATTERNS.md:
- Around line 1457-1460: Remove the stray trailing comma from the property
declaration in the GiftCardDetailsDialog example: locate the class
GiftCardDetailsDialog and the line that declares override val backgroundStyle =
R.style.PrimaryBackground, and change it to a valid Kotlin property declaration
without the comma so it matches the real implementation (also verify override
val forceExpand = true remains unchanged).
In
`@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/PiggyCardsRepository.kt`:
- Around line 385-401: The option-card branch only sets
thisOrder.first().productId, breaking multi-quantity orders; update the branch
where optionGiftcard != null to assign optionGiftcard.id to every order item
(iterate thisOrder and set orderItem.productId = optionGiftcard.id) so all
entries have the correct productId (symbols: optionGiftcard, thisOrder,
productId, orderItem).
- Around line 519-520: The assigned rate currently calls .toString() on a
nullable exchange rate (exchangeRateMap[data.orderId]?.exchangeRate.toString()
?: "0.0"), which yields the literal "null" and bypasses the ?: fallback; update
the rate expression to only call toString() when the exchangeRate is non-null
(e.g., use a safe-call on exchangeRate like
exchangeRateMap[data.orderId]?.exchangeRate?.toString() ?: "0.0" or use let to
map a non-null exchangeRate to its string), so GiftCardInfo.rate never becomes
the string "null".
- Around line 261-325: Add an explicit is DenominationType.MinMaxMajor -> branch
in the when(type) alongside the existing Fixed and MinMax cases (handle it
similarly to MinMax or with the specific denomination mapping required),
populate giftCardMap and return an UpdatedMerchantDetails with appropriate
denominations, denominationsType, discountPercentage (use SERVICE_FEE like the
other branches), redeemType, enabled check (quantity and disabledMerchants) and
productId; additionally, before the fallback else that currently returns an
empty UpdatedMerchantDetails, add a processLogger.warn (or logger.warn)
indicating the merchant id (it.id) and the missing/ mismatched card shape so
failures are visible; finally run ktlint and adjust formatting to satisfy
repository style.
In
`@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsDialog.kt`:
- Around line 493-508: The card number ListItem incorrectly uses the PIN copy
handler (onCopyPin), causing the clipboard label and analytics to say "card
pin"; update the call in GiftCardDetailsDialog (the ListItem block that now
calls onCopyPin(giftCard.number!!)) to use a dedicated card-number copy handler
(e.g., onCopyCardNumber or onCopyNumber) and implement/route that handler to
copy the value with the correct label (replace the "card pin" label in the copy
helper with "card number"); apply the same fix to the debug-only order row that
currently reuses onCopyPin so both places use the new/appropriate card-number
copy callback and label.
In
`@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt`:
- Line 401: In PurchaseGiftCardConfirmDialog.kt, avoid the unsafe (!!) when
calling createSendingRequestFromDashUri with
data.first().paymentUrls?.get("DASH.DASH") by null-checking the payment URL
before calling createSendingRequestFromDashUri; e.g. retrieve the url with a
safe call/get, and if it's null show the existing error/user-facing dialog or
return early instead of proceeding, ensuring createSendingRequestFromDashUri is
only invoked with a non-null String.
- Around line 105-115: Rename the companion val currency to USD_CURRENCY and
update NumberFormat initializers (noCentsFormat and currencyFormat) to set
format.currency = USD_CURRENCY (and set minimumFractionDigits using
USD_CURRENCY.defaultFractionDigits where needed) so the formats use USD
regardless of locale; defensively access paymentUrls (replace
data.first().paymentUrls?.get("DASH.DASH")!! with a safe lookup like
data.first().paymentUrls?.get("DASH.DASH") ?: return/handleError to avoid NPE);
remove or use the unused giftCardId parameter in showGiftCardDetailsDialog
(either consume it or delete the parameter) and delete the dead commented-out
Text composable code to clean up the file.
In
`@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragmentV2.kt`:
- Around line 323-338: In loadMerchant, avoid the two unsafe !! usages by
checking for nulls and early-returning/handling the error: read the providerName
with viewModel.selectedProvider?.name and if null popBackStack/return; after
getting updated from viewModel.updateMerchantDetails(merchant) safely find the
provider with updated.giftCardProviders.find { it.provider == providerName } and
if that result is null popBackStack or show an error and return; only call
viewModel.setIsFixedDenomination(...) when provider is non-null. Use the
existing viewModel methods (selectedProvider, updateMerchantDetails,
setGiftCardMerchant, setIsFixedDenomination) and updated.giftCardProviders to
locate the checks.
---
Nitpick comments:
In
`@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/PiggyCardsRepository.kt`:
- Around line 248-335: The two getMerchant overloads duplicate brand lookup,
disabled-card filtering and card-shape handling; refactor by extracting helpers
such as resolveBrandAndGiftCards(merchantId): Pair<Brand, List<GiftCard>> and
handlers buildFixedMerchantDetails(...), buildOptionMerchantDetails(...),
buildRangeMerchantDetails(... ) that encapsulate the common logic (use symbols
giftCardMap, disabledGiftCards, disabledMerchants, SERVICE_FEE,
UpdatedMerchantDetails), then have getMerchant(merchantId, type:
DenominationType) call those helpers according to DenominationType.Fixed /
MinMax and make the original getMerchant(merchantId) delegate to the overload
(e.g., try Fixed then MinMax) so all filtering, discount math and
enabled/productId logic live in one place.
In
`@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt`:
- Around line 491-507: The function showGiftCardDetailsDialog(txId: Sha256Hash,
giftCardId: String) declares giftCardId but never uses it; remove the unused
parameter from the signature and update its callers (e.g., the call site noted
in the review) to stop passing giftCardId, OR if the dialogs require the
giftCardId for index-aware behavior, pass giftCardId into
GiftCardOrderDetailsDialog.newInstance(...) and
GiftCardDetailsDialog.newInstance(...) and adjust those constructors/factory
methods accordingly (update newInstance signatures and any Dialog classes that
consume it).
- Around line 564-571: Remove the commented-out alternate Text block inside
PurchaseGiftCardConfirmDialog (the eight commented lines that render
uiState.purchaseValueText) because EnterAmount now provides that UI; delete
those commented lines so the file only uses EnterAmount and avoid
duplicated/obsolete code, keeping the dialog implementation clean and preventing
drift.
In
`@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragmentV2.kt`:
- Around line 254-280: Remove the dead/commented blocks that compute total/qty
from denominationQuantities and the earlier viewModel updates: in
PurchaseGiftCardFragmentV2 (inside the onContinue lambda handling mode), delete
the commented sections around lines where denominationQuantities.entries.sumOf {
(d, q) -> d * q } and denominationQuantities.values.sum() are preserved so the
code relies only on the existing FlexibleSingle path that parses amountText and
calls viewModel.setGiftCardOrderInfo(fiat, 1) and the existing multi-card/fixed
paths that set giftCardOrderInfo elsewhere; leave setGiftCardOrderInfo,
amountText, and giftCardOrderInfo references intact and rely on git history if
the old logic is needed.
- Around line 144-196: Hoist the hard-coded 2500 into a named constant (e.g.
GIFT_CARD_PURCHASE_CAP_USD) and replace all literal occurrences in this
fragment: the gating check using totalDouble < 2500 (in the flexibleMultiple
branch), the error branch totalDouble > 2500.0, and the Fiat.parseFiat call that
builds the max message (used as the message argument). Define the constant in a
clear scope (a companion object in PurchaseGiftCardFragmentV2 or a shared
Constants file) and reference it where the code currently uses
2500/2500.0/2500.00.toString(); also apply the same constant to
PurchaseGiftCardConfirmDialog when it’s updated to keep behavior and messaging
consistent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d4a7b6d4-3a2a-4240-bf2c-cccb9857206c
📒 Files selected for processing (14)
.claude/agents/DEVELOPMENT-PATTERNS.md.claude/agents/figma-to-compose.mdcommon/src/main/java/org/dash/wallet/common/ui/components/EnterAmount.ktcommon/src/main/res/drawable/ic_chevron_down_small.xmlfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreSyncWorker.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/PiggyCardsRepository.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/DashSpendViewModel.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragmentV2.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardScreenV2.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsDialog.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsViewModel.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/PiggyCardsTestMerchantData.ktfeatures/exploredash/src/main/res/values/strings-explore-dash.xml
✅ Files skipped from review due to trivial changes (3)
- common/src/main/res/drawable/ic_chevron_down_small.xml
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreSyncWorker.kt
- features/exploredash/src/main/res/values/strings-explore-dash.xml
🚧 Files skipped from review as they are similar to previous changes (4)
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/PiggyCardsTestMerchantData.kt
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/DashSpendViewModel.kt
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardScreenV2.kt
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsViewModel.kt
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt (1)
381-408:⚠️ Potential issue | 🟠 Major | ⚡ Quick winHandle empty purchase responses before calling
first().
purchaseGiftCard()now returns a list, but this path assumes at least one element. If a provider ever returns200 OKwith an empty array, the dialog crashes ondata.first()instead of showing the existing purchase failure UX.Suggested fix
val totalAmount = Coin.valueOf( data.sumOf { if (!it.cryptoAmount.isNullOrEmpty()) { Coin.parseCoin(it.cryptoAmount).value } else { 0L } } ) + val firstCard = data.firstOrNull() + if (firstCard == null) { + hideLoading() + log.error("purchaseGiftCard: empty gift card response") + if (isAdded) { + AdaptiveDialog.create( + R.drawable.ic_error, + getString(R.string.gift_card_purchase_failed), + getString(R.string.gift_card_error), + getString(R.string.button_close) + ).show(requireActivity()) + } + return@launch + } if (!totalAmount.isZero && viewModel.needsCrowdNodeWarning(totalAmount)) { if (!isAdded) { hideLoading() return@launch @@ - val transactionId = createSendingRequestFromDashUri(data.first().paymentUrls?.get("DASH.DASH")!!) + val transactionId = createSendingRequestFromDashUri(firstCard.paymentUrls?.get("DASH.DASH")!!) transactionId?.let { enterAmountViewModel.clearSavedState() viewModel.saveGiftCardDummy(transactionId, data) - showGiftCardDetailsDialog(transactionId, data.first().id) + showGiftCardDetailsDialog(transactionId, firstCard.id) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt` around lines 381 - 408, Before calling data.first() check for an empty list and bail out to the existing purchase-failure flow: if data.isEmpty() then hideLoading(), invoke the same purchase failure UI/handler used elsewhere in this class (the existing "purchase failure" UX or method) and return@launch; do this before the crowd-node warning and before calling createSendingRequestFromDashUri so you never call data.first() or dereference paymentUrls on an empty list (also guard the payment URL lookup or handle a missing "DASH.DASH" entry before passing it to createSendingRequestFromDashUri).
🧹 Nitpick comments (2)
features/exploredash/src/main/res/drawable/ic_stepper_minus.xml (1)
8-8: 💤 Low valueConsider using a theme color attribute instead of a hardcoded hex.
#191C1Fis hardcoded and won't adapt to dark mode or dynamic theming. Referencing a color resource (e.g.,@color/dash_gray_1or?attr/colorOnSurface) keeps the icon consistent with the app's theme system, especially important for a stepper control that appears in interactive UI.🎨 Suggested change
- android:strokeColor="#191C1F" + android:strokeColor="@color/dash_gray_1"(Apply the same fix to
ic_stepper_plus.xml.)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@features/exploredash/src/main/res/drawable/ic_stepper_minus.xml` at line 8, Replace the hardcoded stroke color android:strokeColor="#191C1F" in ic_stepper_minus.xml with a theme-aware color reference (e.g., use a color resource like `@color/dash_gray_1` or an attribute like ?attr/colorOnSurface) so the icon adapts to dark mode and dynamic themes; apply the same change to ic_stepper_plus.xml and ensure both XML drawables reference the chosen color attribute/resource instead of the hex literal.common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountCompose.kt (1)
53-63: ⚡ Quick winGuard
processAmountKeyInput()against unsupported key values.Unknown
keyvalues currently fall intoelseand get appended to the amount string. That can introduce invalid amount states if this helper is reused outside this keypad. Prefer explicit key whitelisting and no-op on unsupported keys.Suggested diff
fun processAmountKeyInput(current: String, key: String, maxDecimalPlaces: Int = 2): String { + val isDigitKey = key.length == 1 && key[0].isDigit() return when (key) { "back" -> if (current.length > 1) current.dropLast(1) else "0" "back_long" -> "0" "." -> if (current.contains('.')) current else "$current." - else -> { + else -> if (isDigitKey) { val result = if (current == "0") key else current + key val dotIndex = result.indexOf('.') if (dotIndex != -1 && result.length - dotIndex - 1 > maxDecimalPlaces) current else result - } + } else current } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountCompose.kt` around lines 53 - 63, The function processAmountKeyInput currently treats any unknown key as a digit and appends it, which can create invalid amounts; update processAmountKeyInput to explicitly whitelist allowed keys (digits 0-9, ".", "back", "back_long") and treat any other key as a no-op returning the current value; implement the digit-path only when key.matches(0-9) and keep existing logic for "." and back/back_long and decimal-place checks so unsupported inputs no longer mutate the amount.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@common/src/main/java/org/dash/wallet/common/ui/components/TopIntroSend.kt`:
- Around line 281-285: Replace the hardcoded TalkBack strings in the
contentDescription for the eye-toggle with localized string resources: add
string keys (e.g., hide_balance and show_balance) to strings.xml and use
stringResource(...) in the TopIntroSend composable where contentDescription is
set (the conditional that checks isVisible) so the accessibility labels are
translated instead of being hardcoded.
In
`@common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountCompose.kt`:
- Around line 119-123: The Icon shown for the backspace key in
EnterAmountCompose (inside the isBack branch rendering
painterResource(R.drawable.ic_delete_backward)) currently uses
contentDescription = null; update this to provide an accessible label by using a
localized string via contentDescription =
stringResource(R.string.accessibility_delete) (and add
R.string.accessibility_delete to your strings.xml if it doesn't exist), so
screen readers can announce the delete/backspace control.
In
`@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/DashSpendViewModel.kt`:
- Around line 455-464: The PiggyCards test-merchant branch calls
(piggyCardsRepository as PiggyCardsRepository).getMerchant(...) but discards the
return value; capture the returned merchant/provider response and add it into
merchantResponseList and providerResponseList in the same way other branches do
so updateMerchantDetailsForAllProviders() sees the PiggyCards data. Locate the
branch guarded by SUPPORT_PIGGY_CARDS_TEST_MERCHANT and
PiggyCardsTestMerchants.ALL.find { it.merchantId == merchant.merchantId } and
after calling getMerchant(provider.sourceId, DenominationType.fromString(...))
append the non-null result into merchantResponseList and providerResponseList
(matching how other code paths populate those collections) so the subsequent
call to updateMerchantDetailsForAllProviders() includes the PiggyCards
test-merchant data.
- Around line 190-197: The public MutableStateFlow properties
isFixedDenominationMultiple and giftCardOrderInfo should be made private
MutableStateFlow-backed properties with public immutable StateFlow accessors
(use asStateFlow()), mirroring the pattern used for
_isFixedDenomination/isFixedDenomination; rename the mutables (e.g.,
_isFixedDenominationMultiple, _giftCardOrderInfo) and expose val
isFixedDenominationMultiple: StateFlow<Boolean?> and val giftCardOrderInfo:
StateFlow<Map<Double,Int>>; remove direct external mutation from
PurchaseGiftCardFragmentV2 by adding ViewModel update methods (e.g.,
setIsFixedDenominationMultiple(value: Boolean?) and updateGiftCardOrderInfo(...)
or specific add/remove methods) that encapsulate and validate state changes
before updating the private MutableStateFlow(s).
In
`@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragmentV2.kt`:
- Around line 348-353: After calling viewModel.updateMerchantDetails(merchant)
the code checks the stale viewModel.giftCardMerchant.value; change the guard to
inspect the returned updated merchant instead (e.g., check updated?.active !=
true) and if it's inactive, popBackStack and return; only call
viewModel.setGiftCardMerchant(updated) after confirming updated is active.
Reference: updateMerchantDetails, updated, viewModel.giftCardMerchant,
viewModel.setGiftCardMerchant.
- Around line 160-167: The enablement logic for
GiftCardPurchaseMode.FlexibleMultiple and Fixed can become true with an empty
cart; add a check that at least one card is selected before allowing Continue.
In the branch handling GiftCardPurchaseMode.FlexibleMultiple and Fixed (look for
denominationQuantities, exceedsInventory, minFiat, totalDouble, fiatBalance,
isBlockchainReplaying and merchant), require denominationQuantities.any {
it.value > 0 } (or denominationQuantities.values.sum() > 0) as an additional AND
condition so the whole expression also ensures there is at least one selected
card.
- Around line 371-389: The generated denominations list in
PurchaseGiftCardFragmentV2.kt can contain duplicates or values outside the
merchant bounds; update the code that builds the denominations (the local
denominations list used to construct GiftCardPurchaseMode.FlexibleMultiple) to:
clamp each candidate value to the range [minimum, maximum], deduplicate them
(preserve order or sort) before creating the FlexibleMultiple, and apply the
same clamping/dedupe logic inside the PiggyCards test-merchant branch as well;
consider deduping doubles with a stable approach (e.g., round to cents or use a
LinkedHashSet of rounded values) so rows aren’t repeated and no denomination
exceeds merchant.maxCardPurchase.
---
Outside diff comments:
In
`@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt`:
- Around line 381-408: Before calling data.first() check for an empty list and
bail out to the existing purchase-failure flow: if data.isEmpty() then
hideLoading(), invoke the same purchase failure UI/handler used elsewhere in
this class (the existing "purchase failure" UX or method) and return@launch; do
this before the crowd-node warning and before calling
createSendingRequestFromDashUri so you never call data.first() or dereference
paymentUrls on an empty list (also guard the payment URL lookup or handle a
missing "DASH.DASH" entry before passing it to createSendingRequestFromDashUri).
---
Nitpick comments:
In
`@common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountCompose.kt`:
- Around line 53-63: The function processAmountKeyInput currently treats any
unknown key as a digit and appends it, which can create invalid amounts; update
processAmountKeyInput to explicitly whitelist allowed keys (digits 0-9, ".",
"back", "back_long") and treat any other key as a no-op returning the current
value; implement the digit-path only when key.matches(0-9) and keep existing
logic for "." and back/back_long and decimal-place checks so unsupported inputs
no longer mutate the amount.
In `@features/exploredash/src/main/res/drawable/ic_stepper_minus.xml`:
- Line 8: Replace the hardcoded stroke color android:strokeColor="#191C1F" in
ic_stepper_minus.xml with a theme-aware color reference (e.g., use a color
resource like `@color/dash_gray_1` or an attribute like ?attr/colorOnSurface) so
the icon adapts to dark mode and dynamic themes; apply the same change to
ic_stepper_plus.xml and ensure both XML drawables reference the chosen color
attribute/resource instead of the hex literal.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4fadd4b4-a1b5-40bf-be83-2917fd02bb2a
📒 Files selected for processing (9)
common/src/main/java/org/dash/wallet/common/ui/components/TopIntroSend.ktcommon/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountCompose.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/DashSpendViewModel.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragmentV2.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardScreenV2.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.ktfeatures/exploredash/src/main/res/drawable/ic_stepper_minus.xmlfeatures/exploredash/src/main/res/drawable/ic_stepper_plus.xmlfeatures/exploredash/src/main/res/values/strings-explore-dash.xml
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt (2)
381-405:⚠️ Potential issue | 🟠 Major | ⚡ Quick winGuard the purchase response before calling
first().
purchaseGiftCard()is an external call, but the success path assumesdatais non-empty. If the backend ever returns[], bothdata.first()calls crash instead of showing the existing failure UI.💡 Minimal fix
val data = try { viewModel.purchaseGiftCard() } catch (ex: CTXSpendException) { hideLoading() when { @@ } return@launch } + + if (data.isEmpty()) { + hideLoading() + log.error("purchaseGiftCard: empty response") + if (isAdded) { + AdaptiveDialog.create( + R.drawable.ic_error, + getString(R.string.gift_card_purchase_failed), + getString(R.string.gift_card_error), + getString(R.string.button_close) + ).show(requireActivity()) + } + return@launch + } val totalAmount = Coin.valueOf(🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt` around lines 381 - 405, The code assumes the purchase response list "data" is non-empty and calls data.first() which can crash; before using data.first() (and before computing totalAmount), guard the response by checking data.isNotEmpty() or using data.firstOrNull() and handle the empty case by calling hideLoading(), showing the existing failure UI/path and returning from the coroutine; update the block around the totalAmount computation, the needsCrowdNodeWarning check, and the createSendingRequestFromDashUri(...) call so they only run when data has at least one element (or handle null transactionId as now).
198-210:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd a re-entrancy guard before starting the purchase flow.
This handler can be entered twice before
showLoading()runs inside the coroutine, which makes duplicate purchase submissions possible on a fast double-tap.💡 Minimal fix
private fun onConfirmButtonClicked() { + if (_uiState.value.isLoading) return + showLoading() viewLifecycleOwner.lifecycleScope.launch { // Double-check merchant is still available before proceeding if (viewModel.giftCardMerchant.value == null) { + hideLoading() log.warn("PurchaseGiftCardConfirmDialog: Merchant became null during confirmation, dismissing") dismiss() return@launch } - showLoading() if (!isAdded || authManager.authenticate(requireActivity()) == null) { hideLoading() return@launch }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt` around lines 198 - 210, The onConfirmButtonClicked handler can be entered twice causing duplicate purchases; add a re-entrancy guard (e.g., a boolean/atomic flag like isPurchasing) checked and set at the start of onConfirmButtonClicked (or immediately inside viewLifecycleOwner.lifecycleScope.launch before showLoading()) and cleared on all exit paths (after hideLoading/dismiss or on failure), so subsequent invocations return early; reference the onConfirmButtonClicked method, showLoading()/hideLoading(), and authManager.authenticate(...) when adding and clearing the guard to ensure duplicate submissions are prevented.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsDialog.kt`:
- Around line 38-40: The file GiftCardDetailsDialog.kt has ktlint failures:
remove the unused Compose import (e.g., delete rememberScrollState import if
unused), remove the trailing comma in the previewState(...) call, break up or
shorten the overlong preview invocations so they fit line-length limits, ensure
the file ends with a newline, and reformat the file with ktlint/ktfmt (or run
the project's ktlintMainSourceSetCheck) to apply consistent formatting; adjust
references in previews and composables as needed (search for previewState(...)
and the preview functions) to satisfy the linter.
- Around line 219-223: The timeout log message is hardcoded to "after 10 tries"
and is out of sync with the actual threshold; update the onErrorLogged call in
the LaunchedEffect block (where uiState.error, uiState.queries,
waitLimitForError are checked) so the message uses the real threshold value
(e.g., interpolate waitLimitForError or the constant WAIT_LIMIT_FOR_ERROR)
instead of the literal "10" to keep logs accurate for support.
---
Outside diff comments:
In
`@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt`:
- Around line 381-405: The code assumes the purchase response list "data" is
non-empty and calls data.first() which can crash; before using data.first() (and
before computing totalAmount), guard the response by checking data.isNotEmpty()
or using data.firstOrNull() and handle the empty case by calling hideLoading(),
showing the existing failure UI/path and returning from the coroutine; update
the block around the totalAmount computation, the needsCrowdNodeWarning check,
and the createSendingRequestFromDashUri(...) call so they only run when data has
at least one element (or handle null transactionId as now).
- Around line 198-210: The onConfirmButtonClicked handler can be entered twice
causing duplicate purchases; add a re-entrancy guard (e.g., a boolean/atomic
flag like isPurchasing) checked and set at the start of onConfirmButtonClicked
(or immediately inside viewLifecycleOwner.lifecycleScope.launch before
showLoading()) and cleared on all exit paths (after hideLoading/dismiss or on
failure), so subsequent invocations return early; reference the
onConfirmButtonClicked method, showLoading()/hideLoading(), and
authManager.authenticate(...) when adding and clearing the guard to ensure
duplicate submissions are prevented.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f6da1651-4976-4a03-a066-d88ab2052078
📒 Files selected for processing (3)
features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsDialog.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardOrderDetailsDialog.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.claude/agents/DEVELOPMENT-PATTERNS.md:
- Around line 1477-1480: The comment for ctxSpendViewModel is incorrect: it says
"Activity-scoped ViewModel..." but the property uses by
viewModels<DashSpendViewModel>() which creates a dialog/fragment-scoped
instance; either change the delegate to by
activityViewModels<DashSpendViewModel>() to make it truly activity-scoped, or
update the comment to explicitly state that ctxSpendViewModel uses by viewModels
and is dialog/fragment-scoped (and note intended usage/fire-and-forget behavior)
so readers aren’t misled.
In
`@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsDialog.kt`:
- Around line 793-795: Replace the hard-coded real-looking card PAN
"6006491727005748" used for the gift card number and barcode in
GiftCardDetailsDialog with an obviously fake test value (e.g., repeating digits
or all zeros) so static analysis won't flag it; update both places where the
variable/field named number and barcode are set to that literal, keeping the
same format as other examples in the file (ensure tests/previews use the new
clearly fake value).
- Around line 120-121: The DashSpendViewModel is being created with dialog scope
using viewModels() in GiftCardDetailsDialog; replace that with the nav-graph
scoped exploreViewModels<DashSpendViewModel>() so the dialog shares the same
DashSpendViewModel instance used by PurchaseGiftCardFragment,
DashSpendTermsDialog, PurchaseGiftCardConfirmDialog, SearchFragment, and so that
calls on ctxSpendViewModel.logError() and ctxSpendViewModel.createEmailIntent()
operate on the shared state. Locate the property declaration private val
ctxSpendViewModel by viewModels<DashSpendViewModel>() in GiftCardDetailsDialog
and change it to use exploreViewModels<DashSpendViewModel>().
In
`@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsViewModel.kt`:
- Around line 437-450: The dummy-card creation copies every field from
cardToCopy which preserves sensitive fields (number, pin, barcodeValue,
merchantUrl/redeemUrl); change the added creation so each new card only inherits
the index and explicitly clears sensitive fields (e.g., set number=null,
pin=null, barcodeValue=null, merchantUrl=null/redeemUrl=null as appropriate)
before calling giftCardDao.insertGiftCard and updating _uiState; also ensure the
updateGiftCard path that writes merchantUrl/redeemUrl explicitly clears
number/pin/barcodeValue for that index so DB entries cannot retain stale data.
- Around line 543-558: The code is indexing uiState.value.giftCards by the
loop/parameter "index" (a position) which can mismatch the GiftCard.index field
and cause OOB or corruption; update the code in functions like updateGiftCard
and the metadataProvider.updateGiftCardMetadata call to locate the card by its
GiftCard.index (e.g., find or first { it.index == index }) rather than using
giftCards[index], and when creating copies (the giftCard.copy(...) calls used to
update number/pin) do not reassign the index field—only copy number and pin (and
other mutable fields) so the stored .index is preserved; also handle the case
where the find returns null (guard or early return) to avoid crashes.
In
`@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragmentV2.kt`:
- Around line 259-263: The try/catch around Fiat.parseFiat in
PurchaseGiftCardFragmentV2 (the fiatAmount assignment and the similar block
later) currently swallows the exception; update both blocks to catch the
exception but log the caught exception (at debug) before returning null so parse
failures are visible in logs during onContinue handling. Locate the
Fiat.parseFiat(...) calls in PurchaseGiftCardFragmentV2 (used to produce
fiatAmount) and add a debug log statement that includes the exception
message/stacktrace via your app's logger (e.g., Timber/Log/fragment logger)
inside the catch, then keep returning null to preserve behavior.
- Line 158: The boolean expression in PurchaseGiftCardFragmentV2 (the line
containing "amount > BigDecimal.ZERO && amount >= min && amount <= max && amount
< balanceMax && !isBlockchainReplaying") exceeds ktlint's 120-char limit; split
the chained conditions across multiple lines (one condition per line or grouped
logically) and align them with indentation so the expression remains under 120
chars, ensuring you keep the same logical order and use the same variable names
(amount, min, max, balanceMax, isBlockchainReplaying) and any surrounding
parentheses in the method where this check appears.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4b8f3afd-c886-4bf3-96ea-5e2cd970496b
📒 Files selected for processing (18)
.claude/agents/DEVELOPMENT-PATTERNS.mdfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreSyncWorker.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/GiftCardDao.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/DashSpendRepository.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/PiggyCardsRepository.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/DashSpendUserAuthFragment.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/DashSpendViewModel.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragment.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragmentV2.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardScreenV2.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsDialog.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardDetailsViewModel.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardOrderDetailsDialog.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardOrderDetailsViewModel.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardViewModel.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/explore/SearchFragment.ktfeatures/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/PiggyCardsTestMerchantData.kt
✅ Files skipped from review due to trivial changes (2)
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardViewModel.kt
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/PiggyCardsTestMerchantData.kt
🚧 Files skipped from review as they are similar to previous changes (12)
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/explore/SearchFragment.kt
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/DashSpendUserAuthFragment.kt
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/DashSpendRepository.kt
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreSyncWorker.kt
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardFragment.kt
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardOrderDetailsViewModel.kt
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/GiftCardOrderDetailsDialog.kt
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/GiftCardDao.kt
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/dialogs/PurchaseGiftCardConfirmDialog.kt
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/PiggyCardsRepository.kt
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/DashSpendViewModel.kt
- features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dashspend/PurchaseGiftCardScreenV2.kt
1. ignoring PC if logged into CTX 2. discount and fees for negative values 3. purchase confirmation dialog clipping buttons
|
|
||
| exploreConfig.saveExploreDatabasePrefs(databasePrefs) | ||
|
|
||
| addPiggyCardsTestMerchantIfNeeded() |
There was a problem hiding this comment.
seems like real users will see test merchants in master?
| val metadataItem = TransactionMetadataCacheItem( | ||
| metadata, | ||
| giftCard | ||
| giftCard.first() |
Issue being fixed or feature implemented
Related PR's and Dependencies
Screenshots / Videos
How Has This Been Tested?
Checklist:
Summary by CodeRabbit
New Features
Refactor
Bug Fixes / Data
Documentation