Skip to content

fix: improve the TX exporters#1452

Merged
HashEngineering merged 12 commits intomasterfrom
fix/csv-exporter
Jan 19, 2026
Merged

fix: improve the TX exporters#1452
HashEngineering merged 12 commits intomasterfrom
fix/csv-exporter

Conversation

@HashEngineering
Copy link
Copy Markdown
Collaborator

@HashEngineering HashEngineering commented Jan 7, 2026

Issue being fixed or feature implemented

Related PR's and Dependencies

Screenshots / Videos

How Has This Been Tested?

  • QA (Mobile Team)

Checklist:

  • I have performed a self-review of my own code and added comments where necessary
  • I have added or updated relevant unit/integration/functional/e2e tests

Summary by CodeRabbit

  • New Features

    • CSV export UI for transaction history with share/send flow and ZenLedger export option (analytics tracked).
  • Database

    • Added gift card providers table and extended migration path.
    • Exposed address-metadata count access.
  • Behavior

    • Export now initializes metadata, shows progress, skips internal/CoinJoin transactions when requested, and handles errors.
  • Chores

    • Updated obfuscation rules for integrations; removed legacy export dialog.

✏️ Tip: You can customize this high-level summary in your review settings.

@HashEngineering HashEngineering self-assigned this Jan 7, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 7, 2026

📝 Walkthrough

Walkthrough

Adds CSV export UI and wiring to load transaction metadata via a provider, introduces AnalyticsConstants.Tools events, creates a DB migration (3→4) that adds a gift_card_providers table, adds AddressMetadataDao.count(), adjusts metadata-merge logic, refactors exporters to use TransactionMetadataProvider, and removes ExportTransactionHistoryDialogBuilder.

Changes

Cohort / File(s) Summary
Analytics & ProGuard
common/src/main/java/org/dash/wallet/common/services/analytics/AnalyticsConstants.kt, wallet/proguard.cfg
Added object Tools with EXPORT_CSV and ZENLEDGER event constants; added ProGuard keep rules for ZenLedger-related classes under de.schildbach.wallet.ui.more.tools.*.
Explore DB & Migrations
features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreDatabase.kt, features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreDatabaseMigrations.kt
Included migration3To4 in DB build path; migration 3→4 now executes CREATE TABLE IF NOT EXISTS gift_card_providers with columns id (PK autoincrement), merchantId, provider, redeemType, savingsPercentage, active, denominationsType, sourceId.
Address Metadata DAO & Usage
wallet/src/de/schildbach/wallet/database/dao/AddressMetadataDao.kt, wallet/src/de/schildbach/wallet/service/WalletTransactionMetadataProvider.kt
Added suspend fun count(): Int to AddressMetadataDao; getAllTransactionMetadata() now checks hasAddressMetadata (via count) and only applies address-derived taxCategory when address metadata exists.
Exporter Core & Metadata Provider
wallet/src/de/schildbach/wallet/transactions/TransactionExporter.kt, wallet/src/de/schildbach/wallet/transactions/CSVExporter.kt, wallet/src/de/schildbach/wallet/transactions/TaxBitExporter.kt
Exporters refactored to accept TransactionMetadataProvider (removed metadata map ctor param); added suspend fun initMetadataMap() and lateinit var metadataMap; export functions made suspend; CSVExporter now detects CoinJoin (CoinJoinTransactionType) and excludes self-recorded/CoinJoin transactions when excludeInternal is true.
CSV Export UI
wallet/src/de/schildbach/wallet/ui/more/ExportCSVDialogFragment.kt
Added ExportCSVDialogFragment and composable ExportCSVDialog to init metadata, generate CSV using a TransactionExporter, write file to cache, and launch a share intent with progress, completion, and error handling.
Tools UI & ViewModels
wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt, wallet/src/de/schildbach/wallet/ui/more/ToolsViewModel.kt, wallet/src/de/schildbach/wallet/ui/more/tools/ZenLedgerViewModel.kt
ToolsViewModel now accepts AnalyticsService, adds logEvent() and a suspend getTransactionExporter() returning TaxBitExporter; ToolsFragment switches to coroutine-driven export flow that disables auto-logout, shows ExportCSVDialogFragment, and logs analytics (including AnalyticsConstants.Tools.ZENLEDGER); ZenLedgerViewModel.sendTransactionInformation() now runs on Dispatchers.IO.
Removal
wallet/src/de/schildbach/wallet/ui/ExportTransactionHistoryDialogBuilder.java
Removed legacy ExportTransactionHistoryDialogBuilder and its export/email flow in favor of the new export dialog and provider-based exporters.
Build config
build.gradle
Added Maven repository Central Portal Snapshots (https://central.sonatype.com/repository/maven-snapshots/).

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant ToolsFragment as ToolsFragment
    participant ToolsVM as ToolsViewModel
    participant ExportDialog as ExportCSVDialog
    participant Exporter as TransactionExporter
    participant Provider as TransactionMetadataProvider
    participant DAO as AddressMetadataDao
    participant FS as File System
    participant Intent as Share Intent

    User->>ToolsFragment: Tap "Export CSV"
    ToolsFragment->>ToolsVM: getTransactionExporter() (suspend)
    ToolsVM-->>ToolsFragment: TaxBitExporter
    ToolsFragment->>ExportDialog: show(exporter)

    rect rgba(220,235,255,0.6)
      ExportDialog->>Exporter: initMetadataMap() (suspend)
      Exporter->>Provider: getAllTransactionMetadata()
      Provider->>DAO: count() and query metadata
      DAO-->>Provider: results
      Provider-->>Exporter: metadata map
    end

    rect rgba(220,255,220,0.6)
      ExportDialog->>Exporter: exportString() (suspend)
      Exporter-->>ExportDialog: CSV content
      ExportDialog->>FS: write CSV file to cache
      FS-->>ExportDialog: file URI
    end

    rect rgba(255,245,220,0.6)
      ExportDialog->>Intent: create ACTION_SEND with URI
      Intent->>User: show chooser
      User-->>Intent: share selected
    end

    ExportDialog->>ToolsFragment: onExportComplete(file)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I nibble code lines, tidy and spry,

CSV carrots in a neat supply,
Metadata hops from provider to field,
A new table sprouts where gift cards yield,
ZenLedger sings — export ready, oh my!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title "fix: improve the TX exporters" is directly related to the main changes, which refactor and improve transaction exporters (CSVExporter, TaxBitExporter, TransactionExporter) with new metadata provider handling, suspend functions, and UI improvements.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In
@features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreDatabaseMigrations.kt:
- Around line 187-207: migration3To4 currently runs "ALTER TABLE merchant ADD
COLUMN denominationsType" which will fail on pre-populated v4 databases that
already contain that column; confirm whether the preloaded v4 schema already has
denominationsType and if so change the migrate implementation of migration3To4
(the anonymous Migration object named migration3To4) to be a no-op (empty body)
like migration2To3 instead of executing the ALTER; leave the CREATE TABLE IF NOT
EXISTS gift_card_providers statement as-is if you still need to create that
table for older databases.

In @wallet/src/de/schildbach/wallet/transactions/TransactionExporter.kt:
- Around line 69-78: The lateinit metadataMap can throw
UninitializedPropertyAccessException if exportString() is called before
initMetadataMap(); make exportString() a suspend function (or add a suspend
overload) and ensure it calls initMetadataMap() when ::metadataMap.isInitialized
is false so initialization is enforced at runtime; update implementations (e.g.,
CSVExporter, TaxBitExporter) to match the suspend signature or to call
initMetadataMap() first, or alternatively add an explicit check at the start of
exportString() that throws a clear IllegalStateException referencing metadataMap
and initMetadataMap() if not initialized.

In @wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt:
- Around line 130-139: The current block disables auto-logout via
(requireActivity() as? SecureActivity)?.turnOffAutoLogout() but if
getTransactionExporter() or ExportCSVDialogFragment().show(...) throws,
turnOnAutoLogout() may never run; wrap the critical section in a try/finally so
that turnOnAutoLogout() is invoked in the finally block (after calling
getTransactionExporter(), logEvent, and showing the dialog), and optionally
catch and log any exceptions around getTransactionExporter() or
ExportCSVDialogFragment().show() before rethrowing or handling, referencing
viewLifecycleOwner.lifecycleScope.launch, getTransactionExporter(),
ExportCSVDialogFragment().show(), turnOffAutoLogout(), and turnOnAutoLogout().
🧹 Nitpick comments (4)
wallet/src/de/schildbach/wallet/ui/more/ToolsViewModel.kt (1)

75-80: Consider returning the interface type TransactionExporter.

The function now returns TaxBitExporter directly instead of the TransactionExporter interface. While this works, returning the interface type would maintain better flexibility and adhere to the dependency inversion principle.

♻️ Proposed refactor
-suspend fun getTransactionExporter(): TransactionExporter = withContext(Dispatchers.IO) {
+suspend fun getTransactionExporter(): TransactionExporter = withContext(Dispatchers.IO) {
     TaxBitExporter(
         transactionMetadataProvider,
         walletData.wallet!!,
-    )
+    ) as TransactionExporter
 }

Or update the return type declaration:

-suspend fun getTransactionExporter(): TransactionExporter = withContext(Dispatchers.IO) {
+suspend fun getTransactionExporter(): TaxBitExporter = withContext(Dispatchers.IO) {
wallet/src/de/schildbach/wallet/ui/more/ExportCSVDialogFragment.kt (2)

69-76: Consider using arguments Bundle instead of instance variables.

The current pattern of setting transactionExporter and dismissFunction via instance variables in the show() method bypasses Android's recommended approach. If a configuration change occurs (e.g., screen rotation) before the dialog is dismissed, these instance variables will be lost, potentially causing null pointer issues.

Consider refactoring to use arguments Bundle or pass serializable/parcelable data through the standard newInstance() pattern with setArguments().


208-225: Remove commented-out code or implement loading UI.

The commented-out loading content section (lines 208-225) suggests incomplete work. If the loading UI is not needed, remove the commented code. If it's intended for future implementation, consider adding a TODO comment or tracking it in an issue.

wallet/src/de/schildbach/wallet/transactions/CSVExporter.kt (1)

55-60: CoinJoin filtering logic is correct; consider simplification.

The logic correctly identifies and excludes CoinJoin mixing transactions from the export. The when expression can be simplified for brevity:

♻️ Suggested simplification
-            val isCoinJoin = when (tx.coinJoinTransactionType) {
-                CoinJoinTransactionType.Mixing -> true
-                else -> false
-            }
+            val isCoinJoin = tx.coinJoinTransactionType == CoinJoinTransactionType.Mixing

Note: This change broadens the exclusion criteria to include CoinJoin transactions alongside self-transactions. Verify this aligns with user expectations for CSV exports.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e7b9f4 and f32dabe.

📒 Files selected for processing (13)
  • common/src/main/java/org/dash/wallet/common/services/analytics/AnalyticsConstants.kt
  • features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreDatabase.kt
  • features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreDatabaseMigrations.kt
  • wallet/proguard.cfg
  • wallet/src/de/schildbach/wallet/database/dao/AddressMetadataDao.kt
  • wallet/src/de/schildbach/wallet/service/WalletTransactionMetadataProvider.kt
  • wallet/src/de/schildbach/wallet/transactions/CSVExporter.kt
  • wallet/src/de/schildbach/wallet/transactions/TaxBitExporter.kt
  • wallet/src/de/schildbach/wallet/transactions/TransactionExporter.kt
  • wallet/src/de/schildbach/wallet/ui/more/ExportCSVDialogFragment.kt
  • wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt
  • wallet/src/de/schildbach/wallet/ui/more/ToolsViewModel.kt
  • wallet/src/de/schildbach/wallet/ui/more/tools/ZenLedgerViewModel.kt
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1417
File: features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/MerchantDao.kt:0-0
Timestamp: 2025-08-08T16:48:49.964Z
Learning: In PR dashpay/dash-wallet#1417, HashEngineering chose to defer adding Room indexes for gift_card_providers (provider, denominationsType, merchantId) to a follow-up PR; do not block the current PR on this optimization. Files: features/exploredash/.../data/explore/MerchantDao.kt and features/exploredash/.../data/dashspend/GiftCardProvider.kt.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1423
File: wallet/src/de/schildbach/wallet/ui/EditProfileActivity.kt:418-446
Timestamp: 2025-08-25T14:48:39.247Z
Learning: HashEngineering prefers to refactor and reuse topup code for balance validation logic improvements in DashPay activities like EditProfileActivity, rather than implementing individual fixes in the current PR.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1422
File: common/src/main/java/org/dash/wallet/common/util/Constants.kt:68-69
Timestamp: 2025-08-25T15:00:20.777Z
Learning: HashEngineering prefers to keep HTTP logging enabled in release mode (using log.info instead of gating with BuildConfig.DEBUG) to debug production errors, even though this may leak URLs in production logs. This is a deliberate trade-off for debugging purposes in the Dash wallet project.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1410
File: wallet/src/de/schildbach/wallet/data/InvitationLinkData.kt:79-84
Timestamp: 2025-07-12T07:12:04.769Z
Learning: HashEngineering prefers to handle defensive validation improvements for URI parsing in follow-up PRs rather than including them in the current PR when the main focus is on replacing Firebase with AppsFlyer.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1425
File: wallet/src/de/schildbach/wallet/ui/more/SettingsScreen.kt:122-151
Timestamp: 2025-09-05T06:47:44.508Z
Learning: HashEngineering prefers to defer UI edge case fixes like the CoinJoin finishing state display issue (showing "Finishing ...%" when mode is OFF but status is FINISHING) to later PRs when the main focus is on redesigning the settings screen.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1422
File: wallet/src/de/schildbach/wallet/data/PaymentIntent.java:476-481
Timestamp: 2025-08-25T15:04:18.461Z
Learning: HashEngineering prefers to keep payeeUsername and payeeUserId in PaymentIntent.toString() logs unredacted for debugging purposes, prioritizing troubleshooting capability over PII protection concerns in the Dash wallet project.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1425
File: common/src/main/java/org/dash/wallet/common/ui/components/DashButton.kt:94-101
Timestamp: 2025-09-05T06:00:25.088Z
Learning: HashEngineering prefers to fix the DashButton alpha dimming issue (using explicit disabled colors instead of overall alpha) in a follow-up PR rather than including it in the current settings screen redesign PR.
📚 Learning: 2025-04-19T07:01:17.535Z
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1362
File: wallet/src/de/schildbach/wallet/ui/more/SettingsViewModel.kt:107-115
Timestamp: 2025-04-19T07:01:17.535Z
Learning: In the Dash Wallet app, DashPayConfig methods like isTransactionMetadataInfoShown() already use withContext(Dispatchers.IO) internally, so wrapping these calls with another withContext(Dispatchers.IO) in ViewModels is redundant.

Applied to files:

  • wallet/src/de/schildbach/wallet/ui/more/tools/ZenLedgerViewModel.kt
  • wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt
  • wallet/src/de/schildbach/wallet/ui/more/ToolsViewModel.kt
  • wallet/src/de/schildbach/wallet/transactions/TransactionExporter.kt
📚 Learning: 2025-04-19T07:01:17.535Z
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1362
File: wallet/src/de/schildbach/wallet/ui/more/SettingsViewModel.kt:107-115
Timestamp: 2025-04-19T07:01:17.535Z
Learning: In Dash Wallet, DataStore operations (like those in DashPayConfig through BaseConfig) already handle background threading internally. Adding another layer of withContext(Dispatchers.IO) around these calls in ViewModels is redundant because the Jetpack DataStore API automatically dispatches its operations to a background thread.

Applied to files:

  • wallet/src/de/schildbach/wallet/ui/more/tools/ZenLedgerViewModel.kt
  • wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt
  • wallet/src/de/schildbach/wallet/ui/more/ToolsViewModel.kt
  • wallet/src/de/schildbach/wallet/transactions/TransactionExporter.kt
📚 Learning: 2025-09-23T05:19:47.861Z
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1428
File: wallet/src/de/schildbach/wallet/ui/dashpay/PlatformRepo.kt:1065-1068
Timestamp: 2025-09-23T05:19:47.861Z
Learning: The Dash wallet app doesn't use non-encrypted wallets, so getWalletEncryptionKey() will never return null in practice. Defensive null-safe programming with ?.let is appropriate for methods like getIdentityFromPublicKeyId() in PlatformRepo.kt.

Applied to files:

  • wallet/src/de/schildbach/wallet/ui/more/tools/ZenLedgerViewModel.kt
  • wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt
  • wallet/src/de/schildbach/wallet/ui/more/ToolsViewModel.kt
  • wallet/src/de/schildbach/wallet/transactions/TransactionExporter.kt
📚 Learning: 2025-05-07T14:19:08.146Z
Learnt from: Syn-McJ
Repo: dashpay/dash-wallet PR: 1389
File: features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/model/Merchant.kt:52-52
Timestamp: 2025-05-07T14:19:08.146Z
Learning: In the dash-wallet project, database migrations must account for pre-populated databases. When new columns are added to model classes, empty migrations with incremented version numbers are used instead of ALTER TABLE statements, as the downloaded database will already contain the new columns. This prevents SQL errors when trying to add columns that already exist.

Applied to files:

  • features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreDatabaseMigrations.kt
  • features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreDatabase.kt
📚 Learning: 2025-08-08T16:48:49.964Z
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1417
File: features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/MerchantDao.kt:0-0
Timestamp: 2025-08-08T16:48:49.964Z
Learning: In PR dashpay/dash-wallet#1417, HashEngineering chose to defer adding Room indexes for gift_card_providers (provider, denominationsType, merchantId) to a follow-up PR; do not block the current PR on this optimization. Files: features/exploredash/.../data/explore/MerchantDao.kt and features/exploredash/.../data/dashspend/GiftCardProvider.kt.

Applied to files:

  • features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreDatabaseMigrations.kt
  • features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreDatabase.kt
📚 Learning: 2025-05-07T14:18:11.161Z
Learnt from: Syn-McJ
Repo: dashpay/dash-wallet PR: 1389
File: features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/explore/SearchFragment.kt:45-46
Timestamp: 2025-05-07T14:18:11.161Z
Learning: In the ExploreViewModel of the dash-wallet application, `appliedFilters` is a StateFlow (not LiveData), so Flow operators like `distinctUntilChangedBy` can be used with it.

Applied to files:

  • wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt
  • wallet/src/de/schildbach/wallet/ui/more/ToolsViewModel.kt
📚 Learning: 2025-05-06T15:46:59.440Z
Learnt from: Syn-McJ
Repo: dashpay/dash-wallet PR: 1386
File: wallet/src/de/schildbach/wallet/ui/send/SendCoinsViewModel.kt:108-0
Timestamp: 2025-05-06T15:46:59.440Z
Learning: In UI code where frequent updates to a value might trigger expensive operations (like the SendCoinsViewModel's `executeDryrun` method), it's preferable to use a Flow with debounce rather than launching a new coroutine for each update. This prevents race conditions, reduces resource usage, and provides a more reactive architecture.

Applied to files:

  • wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt
  • wallet/src/de/schildbach/wallet/transactions/TransactionExporter.kt
📚 Learning: 2025-04-04T07:11:07.664Z
Learnt from: Syn-McJ
Repo: dashpay/dash-wallet PR: 1370
File: fastlane/Fastfile:176-176
Timestamp: 2025-04-04T07:11:07.664Z
Learning: The explore feature in the dash-wallet application uses different naming conventions for different types of databases: preloaded and test databases still use the original naming convention (e.g., "explore.db"), while production databases use the v2 naming scheme (e.g., "explore-v2.db"). This inconsistency is intentional.

Applied to files:

  • features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreDatabase.kt
🧬 Code graph analysis (1)
wallet/src/de/schildbach/wallet/ui/more/ExportCSVDialogFragment.kt (3)
common/src/main/java/org/dash/wallet/common/ui/components/ComposeHostFrameLayout.kt (1)
  • setContent (18-24)
wallet/src/de/schildbach/wallet/ui/BaseMenuActivity.kt (1)
  • startActivity (53-56)
common/src/main/java/org/dash/wallet/common/ui/components/DashModelDialog.kt (1)
  • ModalDialog (43-220)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: check
  • GitHub Check: build
🔇 Additional comments (17)
common/src/main/java/org/dash/wallet/common/services/analytics/AnalyticsConstants.kt (1)

89-92: LGTM!

The new analytics constants follow the established naming convention and are within the 40-character limit as required by the comment on line 20.

wallet/proguard.cfg (1)

241-248: LGTM!

The ProGuard keep rules for ZenLedger classes follow the established pattern in this configuration file and are necessary to prevent obfuscation of data transfer objects used for serialization/deserialization.

wallet/src/de/schildbach/wallet/service/WalletTransactionMetadataProvider.kt (1)

495-510: Performance optimization with pre-check.

The addition of the hasAddressMetadata pre-check avoids unnecessary getDefaultTaxCategory() calls when the address metadata table is empty, improving performance for the common case where no address metadata exists.

features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreDatabase.kt (2)

131-135: LGTM!

The migration3To4 is correctly added to the preload path alongside the existing migrations.


171-175: LGTM!

The migration3To4 is correctly added to the database builder path, maintaining consistency with the preload path.

wallet/src/de/schildbach/wallet/database/dao/AddressMetadataDao.kt (1)

48-50: LGTM!

The count function is correctly implemented with proper SQL syntax and an appropriate return type. The suspend modifier aligns with Room's coroutine support.

wallet/src/de/schildbach/wallet/ui/more/ToolsViewModel.kt (2)

36-36: LGTM!

The analytics service dependency is properly injected via constructor, following Hilt best practices.

Also applies to: 48-48


95-97: LGTM!

The logEvent wrapper correctly forwards analytics events to the service with an empty parameter map.

wallet/src/de/schildbach/wallet/ui/more/tools/ZenLedgerViewModel.kt (1)

50-106: LGTM!

Wrapping the function body in withContext(Dispatchers.IO) is appropriate for the network calls to ZenLedger API and wallet operations. The try-catch error handling is correctly maintained within the IO context.

wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt (3)

33-33: LGTM!

The new imports support the enhanced export flow with timing measurement and auto-logout management.

Also applies to: 47-47


96-96: LGTM!

Switching from lifecycleScope to viewLifecycleOwner.lifecycleScope is the correct pattern for fragments. This ensures coroutines are cancelled when the view is destroyed, preventing potential memory leaks.

Also applies to: 143-143, 151-151


163-163: LGTM!

Analytics tracking for the ZenLedger export is properly implemented and consistent with other analytics events in this file.

wallet/src/de/schildbach/wallet/transactions/TaxBitExporter.kt (1)

25-32: LGTM!

The migration from direct metadata map injection to the provider pattern improves separation of concerns and enables lazy loading of transaction metadata. This aligns with the broader refactoring goals outlined in the PR.

wallet/src/de/schildbach/wallet/ui/more/ExportCSVDialogFragment.kt (2)

161-199: Well-structured export logic with proper async handling.

The export flow correctly:

  • Uses Dispatchers.IO for blocking operations (metadata loading, CSV generation, file I/O)
  • Employs .use{} for automatic resource management of OutputStreamWriter
  • Handles errors with try-catch and callbacks
  • Manages loading state appropriately

112-140: The FileProvider authority in the code ("${requireContext().packageName}.file_attachment") correctly matches the manifest configuration (android:authorities="${applicationId}.file_attachment"). Both resolve to the same package name at runtime, so there is no configuration mismatch.

wallet/src/de/schildbach/wallet/transactions/TransactionExporter.kt (1)

80-89: Proper null-safe confidence handling.

The updated sortedTransactions correctly:

  • Uses lazy initialization for the computed property
  • Safely accesses it.getConfidence(wallet.context) with null checks
  • Falls back to updateTime when confidence.sentAt is unavailable
wallet/src/de/schildbach/wallet/transactions/CSVExporter.kt (1)

26-31: Constructor properly refactored to use provider pattern.

The constructor correctly propagates the TransactionMetadataProvider to the superclass, aligning with the metadata provider refactoring across the export flow.

Comment thread wallet/src/de/schildbach/wallet/transactions/TransactionExporter.kt
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @wallet/src/de/schildbach/wallet/ui/more/ExportCSVDialogFragment.kt:
- Around line 90-94: The onError lambda currently just logs the exception and
dismisses the dialog (see onError = { error -> log.error("Failed to export CSV",
error); dismiss(); dismissFunction?.invoke() }), leaving the user without
feedback; modify onError to show a user-facing error (e.g., a Toast or an
AlertDialog with a brief message that includes a friendly hint and optionally
error.localizedMessage) before calling dismiss() and dismissFunction?.invoke(),
ensuring the UI context (fragment.requireContext() or view?.context) is used
safely when creating the Toast/Dialog.

In @wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt:
- Around line 130-142: The catch block in the export flow swallows exceptions
and the Stopwatch started in the coroutine is never stopped or logged; update
the coroutine in ToolsFragment so that after calling
viewModel.getTransactionExporter() and before showing ExportCSVDialogFragment
you stop the Stopwatch and log the elapsed time (e.g., via viewModel.logEvent or
a logger), and in the catch block log the exception details and surface user
feedback (e.g., show a Toast or an AlertDialog) while still ensuring
SecureActivity.turnOnAutoLogout() is invoked; ensure you reference the existing
symbols Stopwatch, viewModel.getTransactionExporter(),
viewModel.logEvent(AnalyticsConstants.Tools.EXPORT_CSV),
ExportCSVDialogFragment(), and (requireActivity() as?
SecureActivity)?.turnOnAutoLogout()/turnOffAutoLogout() when making these edits.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f32dabe and 90e6501.

📒 Files selected for processing (5)
  • wallet/src/de/schildbach/wallet/transactions/CSVExporter.kt
  • wallet/src/de/schildbach/wallet/transactions/TransactionExporter.kt
  • wallet/src/de/schildbach/wallet/ui/ExportTransactionHistoryDialogBuilder.java
  • wallet/src/de/schildbach/wallet/ui/more/ExportCSVDialogFragment.kt
  • wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt
💤 Files with no reviewable changes (1)
  • wallet/src/de/schildbach/wallet/ui/ExportTransactionHistoryDialogBuilder.java
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1417
File: features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/MerchantDao.kt:0-0
Timestamp: 2025-08-08T16:48:49.964Z
Learning: In PR dashpay/dash-wallet#1417, HashEngineering chose to defer adding Room indexes for gift_card_providers (provider, denominationsType, merchantId) to a follow-up PR; do not block the current PR on this optimization. Files: features/exploredash/.../data/explore/MerchantDao.kt and features/exploredash/.../data/dashspend/GiftCardProvider.kt.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1423
File: wallet/src/de/schildbach/wallet/ui/EditProfileActivity.kt:418-446
Timestamp: 2025-08-25T14:48:39.247Z
Learning: HashEngineering prefers to refactor and reuse topup code for balance validation logic improvements in DashPay activities like EditProfileActivity, rather than implementing individual fixes in the current PR.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1422
File: common/src/main/java/org/dash/wallet/common/util/Constants.kt:68-69
Timestamp: 2025-08-25T15:00:20.777Z
Learning: HashEngineering prefers to keep HTTP logging enabled in release mode (using log.info instead of gating with BuildConfig.DEBUG) to debug production errors, even though this may leak URLs in production logs. This is a deliberate trade-off for debugging purposes in the Dash wallet project.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1410
File: wallet/src/de/schildbach/wallet/data/InvitationLinkData.kt:79-84
Timestamp: 2025-07-12T07:12:04.769Z
Learning: HashEngineering prefers to handle defensive validation improvements for URI parsing in follow-up PRs rather than including them in the current PR when the main focus is on replacing Firebase with AppsFlyer.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1425
File: wallet/src/de/schildbach/wallet/ui/more/SettingsScreen.kt:122-151
Timestamp: 2025-09-05T06:47:44.508Z
Learning: HashEngineering prefers to defer UI edge case fixes like the CoinJoin finishing state display issue (showing "Finishing ...%" when mode is OFF but status is FINISHING) to later PRs when the main focus is on redesigning the settings screen.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1422
File: wallet/src/de/schildbach/wallet/data/PaymentIntent.java:476-481
Timestamp: 2025-08-25T15:04:18.461Z
Learning: HashEngineering prefers to keep payeeUsername and payeeUserId in PaymentIntent.toString() logs unredacted for debugging purposes, prioritizing troubleshooting capability over PII protection concerns in the Dash wallet project.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1425
File: common/src/main/java/org/dash/wallet/common/ui/components/DashButton.kt:94-101
Timestamp: 2025-09-05T06:00:25.088Z
Learning: HashEngineering prefers to fix the DashButton alpha dimming issue (using explicit disabled colors instead of overall alpha) in a follow-up PR rather than including it in the current settings screen redesign PR.
📚 Learning: 2025-04-19T07:01:17.535Z
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1362
File: wallet/src/de/schildbach/wallet/ui/more/SettingsViewModel.kt:107-115
Timestamp: 2025-04-19T07:01:17.535Z
Learning: In the Dash Wallet app, DashPayConfig methods like isTransactionMetadataInfoShown() already use withContext(Dispatchers.IO) internally, so wrapping these calls with another withContext(Dispatchers.IO) in ViewModels is redundant.

Applied to files:

  • wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt
  • wallet/src/de/schildbach/wallet/transactions/TransactionExporter.kt
📚 Learning: 2025-09-23T05:19:47.861Z
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1428
File: wallet/src/de/schildbach/wallet/ui/dashpay/PlatformRepo.kt:1065-1068
Timestamp: 2025-09-23T05:19:47.861Z
Learning: The Dash wallet app doesn't use non-encrypted wallets, so getWalletEncryptionKey() will never return null in practice. Defensive null-safe programming with ?.let is appropriate for methods like getIdentityFromPublicKeyId() in PlatformRepo.kt.

Applied to files:

  • wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt
  • wallet/src/de/schildbach/wallet/transactions/TransactionExporter.kt
📚 Learning: 2025-05-07T14:18:11.161Z
Learnt from: Syn-McJ
Repo: dashpay/dash-wallet PR: 1389
File: features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/explore/SearchFragment.kt:45-46
Timestamp: 2025-05-07T14:18:11.161Z
Learning: In the ExploreViewModel of the dash-wallet application, `appliedFilters` is a StateFlow (not LiveData), so Flow operators like `distinctUntilChangedBy` can be used with it.

Applied to files:

  • wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt
📚 Learning: 2025-05-06T15:46:59.440Z
Learnt from: Syn-McJ
Repo: dashpay/dash-wallet PR: 1386
File: wallet/src/de/schildbach/wallet/ui/send/SendCoinsViewModel.kt:108-0
Timestamp: 2025-05-06T15:46:59.440Z
Learning: In UI code where frequent updates to a value might trigger expensive operations (like the SendCoinsViewModel's `executeDryrun` method), it's preferable to use a Flow with debounce rather than launching a new coroutine for each update. This prevents race conditions, reduces resource usage, and provides a more reactive architecture.

Applied to files:

  • wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt
  • wallet/src/de/schildbach/wallet/transactions/TransactionExporter.kt
📚 Learning: 2025-04-19T07:01:17.535Z
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1362
File: wallet/src/de/schildbach/wallet/ui/more/SettingsViewModel.kt:107-115
Timestamp: 2025-04-19T07:01:17.535Z
Learning: In Dash Wallet, DataStore operations (like those in DashPayConfig through BaseConfig) already handle background threading internally. Adding another layer of withContext(Dispatchers.IO) around these calls in ViewModels is redundant because the Jetpack DataStore API automatically dispatches its operations to a background thread.

Applied to files:

  • wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt
  • wallet/src/de/schildbach/wallet/transactions/TransactionExporter.kt
🪛 detekt (1.23.8)
wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt

[warning] 139-139: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

🔇 Additional comments (9)
wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt (3)

96-96: Good refactor: proper lifecycle scope usage.

Switching to viewLifecycleOwner.lifecycleScope ensures coroutines are properly tied to the Fragment's view lifecycle, preventing leaks if the Fragment is detached while the coroutine is running.


146-148: Good refactor: consistent lifecycle scope usage.

These additional conversions to viewLifecycleOwner.lifecycleScope maintain consistency with the earlier change at Line 96 and properly tie coroutines to the Fragment's view lifecycle.

Also applies to: 154-163


166-166: Good addition: analytics tracking.

Logging the ZenLedger export event provides useful usage metrics for this feature.

wallet/src/de/schildbach/wallet/ui/more/ExportCSVDialogFragment.kt (2)

100-128: LGTM: Proper FileProvider and intent usage.

The implementation correctly uses FileProvider to create a shareable URI for the CSV file and properly configures the ACTION_SEND intent with the necessary permissions. Error logging is appropriate for this final step in the export flow.


149-184: LGTM: Proper async handling and IO dispatching.

The export flow correctly:

  • Uses withContext(Dispatchers.IO) for blocking operations (metadata loading, CSV generation, file writing)
  • Manages loading state properly with cleanup in both success and error paths
  • Null-checks the transactionExporter before proceeding
  • Delegates error handling to the callback
wallet/src/de/schildbach/wallet/transactions/TransactionExporter.kt (2)

38-42: Good refactor: provider-based metadata loading.

Switching from passing a pre-loaded metadataMap to accepting a TransactionMetadataProvider enables lazy loading of metadata, improving memory efficiency and startup performance for the export flow.


170-170: Good API evolution: suspend for async operations.

Making exportString() a suspend function properly reflects its asynchronous nature and enables efficient metadata loading and CSV generation without blocking the main thread.

wallet/src/de/schildbach/wallet/transactions/CSVExporter.kt (2)

26-31: LGTM: Consistent provider-based wiring.

The constructor properly accepts and forwards the TransactionMetadataProvider to the superclass, maintaining consistency with the refactored TransactionExporter API.


49-68: LGTM: Proper CoinJoin transaction filtering.

The implementation correctly:

  • Makes exportString() a suspend function for async operations
  • Identifies CoinJoin mixing transactions using the transaction type
  • Excludes both self-transactions and CoinJoin transactions when excludeInternal is true

The filtering logic ensures that internal wallet movements and mixing operations are appropriately excluded from exports.

Comment thread wallet/src/de/schildbach/wallet/ui/more/ExportCSVDialogFragment.kt
Comment thread wallet/src/de/schildbach/wallet/ui/more/ToolsFragment.kt
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @build.gradle:
- Around line 79-82: The maven repository block named 'Central Portal Snapshots'
with URL 'https://central.sonatype.com/repository/maven-snapshots/' is invalid
and should be removed or replaced; update the build.gradle by deleting that
maven { name 'Central Portal Snapshots' url
'https://central.sonatype.com/repository/maven-snapshots/' } block or change the
url to a valid repository such as 'https://repo1.maven.org/maven2/' so Gradle
can resolve dependencies.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59eb4b8 and fe5e6ac.

📒 Files selected for processing (1)
  • build.gradle
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1417
File: features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/MerchantDao.kt:0-0
Timestamp: 2025-08-08T16:48:49.964Z
Learning: In PR dashpay/dash-wallet#1417, HashEngineering chose to defer adding Room indexes for gift_card_providers (provider, denominationsType, merchantId) to a follow-up PR; do not block the current PR on this optimization. Files: features/exploredash/.../data/explore/MerchantDao.kt and features/exploredash/.../data/dashspend/GiftCardProvider.kt.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1423
File: wallet/src/de/schildbach/wallet/ui/EditProfileActivity.kt:418-446
Timestamp: 2025-08-25T14:48:39.247Z
Learning: HashEngineering prefers to refactor and reuse topup code for balance validation logic improvements in DashPay activities like EditProfileActivity, rather than implementing individual fixes in the current PR.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1422
File: common/src/main/java/org/dash/wallet/common/util/Constants.kt:68-69
Timestamp: 2025-08-25T15:00:20.777Z
Learning: HashEngineering prefers to keep HTTP logging enabled in release mode (using log.info instead of gating with BuildConfig.DEBUG) to debug production errors, even though this may leak URLs in production logs. This is a deliberate trade-off for debugging purposes in the Dash wallet project.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1410
File: wallet/src/de/schildbach/wallet/data/InvitationLinkData.kt:79-84
Timestamp: 2025-07-12T07:12:04.769Z
Learning: HashEngineering prefers to handle defensive validation improvements for URI parsing in follow-up PRs rather than including them in the current PR when the main focus is on replacing Firebase with AppsFlyer.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1425
File: wallet/src/de/schildbach/wallet/ui/more/SettingsScreen.kt:122-151
Timestamp: 2025-09-05T06:47:44.508Z
Learning: HashEngineering prefers to defer UI edge case fixes like the CoinJoin finishing state display issue (showing "Finishing ...%" when mode is OFF but status is FINISHING) to later PRs when the main focus is on redesigning the settings screen.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1422
File: wallet/src/de/schildbach/wallet/data/PaymentIntent.java:476-481
Timestamp: 2025-08-25T15:04:18.461Z
Learning: HashEngineering prefers to keep payeeUsername and payeeUserId in PaymentIntent.toString() logs unredacted for debugging purposes, prioritizing troubleshooting capability over PII protection concerns in the Dash wallet project.
Learnt from: HashEngineering
Repo: dashpay/dash-wallet PR: 1425
File: common/src/main/java/org/dash/wallet/common/ui/components/DashButton.kt:94-101
Timestamp: 2025-09-05T06:00:25.088Z
Learning: HashEngineering prefers to fix the DashButton alpha dimming issue (using explicit disabled colors instead of overall alpha) in a follow-up PR rather than including it in the current settings screen redesign PR.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: check

Comment thread build.gradle
@HashEngineering HashEngineering merged commit 164a79f into master Jan 19, 2026
3 checks passed
@HashEngineering HashEngineering deleted the fix/csv-exporter branch April 20, 2026 13:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants