Skip to content

Conversation

@praveenperera
Copy link
Contributor

@praveenperera praveenperera commented Dec 20, 2025

Summary by CodeRabbit

  • New Features

    • QR export for labels with density options.
    • CSV export for transactions.
    • Unified label-sharing export that returns filename + content.
    • Conditional loading popups shown for longer-running exports.
  • Refactor

    • Simplified mobile export flows—removed delayed/popups and complex state in favor of direct export + standard alerts/snackbars.
    • QR generation converted to async for smoother UI and lifecycle handling.
    • Streamlined share and cleanup flow.
  • Bug Fixes

    • More graceful handling of update/send failures to avoid crashes.

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

@coderabbitai
Copy link

coderabbitai bot commented Dec 20, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a loading-popup helper in Rust that conditionally shows a short-lived UI spinner around long-running exports, exposes new async label/transaction export APIs via the Rust wallet manager and FFI, updates reconcile messages for popup control, and simplifies mobile export/QR UI flows to use the new APIs.

Changes

Cohort / File(s) Summary
Rust: loading popup & reconcile
rust/src/loading_popup.rs, rust/src/app/reconcile.rs, rust/src/lib.rs
New with_loading_popup that races an operation vs a short delay and enforces a minimum display time; sends ShowLoadingPopup/HideLoadingPopup. Added enum variants and module registration; send_update failures now logged.
Rust: wallet exports & task helper
rust/src/manager/wallet_manager.rs, rust/src/label_manager.rs, rust/src/task.rs
New public methods: export_labels_for_share(), export_labels_for_qr(density), export_transactions_csv() returning content+filename types; label exports and BBQr generation made async and offloaded to blocking tasks; added spawn_blocking wrapper; new LabelExportResult/TransactionExportResult types.
Kotlin FFI / bindings
android/app/src/main/java/org/bitcoinppl/.../cove.kt
Added FFI converters and async uniffi wrappers for new export result types and transaction export; added checksum validation for the new FFI methods and converters.
Android: reconcile + export UI
android/app/src/main/java/org/bitcoinppl/cove/AppManager.kt, .../wallet/MoreInfoPopover.kt, .../wallet/WalletSheets.kt, .../views/QrExportView.kt
AppManager handles ShowLoadingPopup/HideLoadingPopup. Export UI removed local delayed loading-alert logic and now calls exportTransactionsCsv / exportLabelsForShare and uses returned content/filename for sharing; QR generation functions converted to suspend and simplified.
iOS: reconcile + export UI
ios/Cove/AppManager.swift, ios/Cove/Flows/SelectedWalletFlow/MoreInfoPopover.swift, .../SelectedWalletScreen.swift, ios/Cove/Views/QrExportView.swift
AppManager presents/dismisses loading popup for new messages. Export flows simplified to await new async Rust exports directly, removed local loading tasks/delays, converted QR generation closures to async/throws, added showQrExport().
Android/iOS UI: QR export signature updates
android/.../views/QrExportView.kt, ios/.../Views/QrExportView.swift
Changed QR generation closures to async/suspend (generateBbqrStrings, generateUrStrings) and updated call sites to use a single async task flow for QR generation.

Sequence Diagram(s)

sequenceDiagram
    participant UI as Mobile UI (Android / iOS)
    participant FFI as Platform FFI (Kotlin / Swift)
    participant Rust as Rust WalletManager
    participant Updater as Updater/Reconciler
    participant Op as Export Operation

    UI->>FFI: user triggers export (share / QR / CSV)
    FFI->>Rust: uniffi async call export_*
    Rust->>Op: start export operation
    Rust->>Rust: start short delay timer
    par concurrent
        Op->>Op: perform export work (blocking/async)
        Rust->>Rust: await delay
    end
    alt operation finishes before delay
        Op-->>Rust: return result (content, filename)
        Rust-->>FFI: return result (no popup)
        FFI-->>UI: present ShareSheet / QR
    else operation exceeds delay
        Rust->>Updater: send_update(ShowLoadingPopup)
        Updater->>UI: show loading popup
        Op-->>Rust: return result
        Rust->>Rust: ensure minimum visible time
        Rust->>Updater: send_update(HideLoadingPopup)
        Updater->>UI: hide loading popup
        Rust-->>FFI: return result
        FFI-->>UI: present ShareSheet / QR
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

  • Pay attention to:
    • rust/src/loading_popup.rs — timing, select/race correctness, and Updater message ordering.
    • rust/src/manager/wallet_manager.rs & rust/src/label_manager.rs — async/blocking boundaries, error mapping, filename sanitization.
    • android/.../cove.kt — FFI converters, memory layout, checksum validation correctness.
    • Mobile UI changes — removal of local alert/delay logic and correctness of new popup-driven flow.
    • rust/src/task.rs — safety and runtime assumptions in spawn_blocking.

Possibly related PRs

Suggested labels

android

"I’m a rabbit in a code-filled glen,
I race a timer, then pop up when things slow.
Exports hum, QR strings hop along,
A tiny spinner dances, then away we go. 🐇"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.15% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: introduction of a conditional loading popup mechanism in Rust that communicates with platform-specific UI layers via new AppStateReconcileMessage variants.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch rust-conditional-loading-popup

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.

@greptile-apps
Copy link

greptile-apps bot commented Dec 20, 2025

Greptile Summary

This PR centralizes loading popup logic into Rust, replacing platform-specific implementations with a unified conditional loading popup system. The new with_loading_popup helper only shows popups for operations exceeding 125ms, with a 350ms minimum display time to prevent flashing.

Key improvements:

  • Removed ~150 lines of complex loading state management from iOS/Android
  • Export operations (labels, transactions, QR codes) now use async methods with spawn_blocking for CPU-intensive work
  • New unified API returns both content and filename together (LabelExportResult, TransactionExportResult)
  • Biased select pattern ensures fast operations never trigger popup UI
  • Better error handling in Updater::send_update (logs instead of panicking on disconnected frontend)

All export flows simplified to single async call + error handling, delegating loading UX to Rust layer.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • Well-architected refactoring that consolidates complex logic into Rust layer. Proper async/await patterns, spawn_blocking for CPU work, graceful error handling. The biased select pattern is correct. No breaking changes to public APIs. Simplifies platform code significantly while maintaining equivalent functionality.
  • No files require special attention

Important Files Changed

Filename Overview
rust/src/loading_popup.rs New module implementing conditional loading popup with smart delays - well-designed with biased select for fast operations
rust/src/manager/wallet_manager.rs New async export methods wrap operations with loading popup helper - returns filename + content together for cleaner API
ios/Cove/Flows/SelectedWalletFlow/MoreInfoPopover.swift Simplified export flow by removing manual loading state management - delegates to Rust-side conditional popup
android/app/src/main/java/org/bitcoinppl/cove/wallet/MoreInfoPopover.kt Removed complex loading alert management and delays - now uses async Rust methods with built-in loading popup
android/app/src/main/java/org/bitcoinppl/cove/wallet/WalletSheets.kt Updated to use new async export methods - exportLabelManager cleanup moved but QR export still uses it correctly

Sequence Diagram

sequenceDiagram
    participant UI as UI Layer (iOS/Android)
    participant Rust as Rust Manager
    participant LoadingPopup as with_loading_popup
    participant LabelMgr as LabelManager
    participant TaskPool as spawn_blocking
    participant Frontend as Frontend (Updater)

    Note over UI,Frontend: Export Labels for QR Code Flow

    UI->>Rust: exportLabelsForQr(density)
    Rust->>LoadingPopup: with_loading_popup(async { ... })
    
    par Biased Select (Fast Path vs Delayed Popup)
        LoadingPopup->>LabelMgr: export_to_bbqr_with_density()
        LabelMgr->>LabelMgr: export() (async)
        LabelMgr->>TaskPool: spawn_blocking(db.labels.all_labels())
        TaskPool-->>LabelMgr: labels
        LabelMgr->>TaskPool: spawn_blocking(BBQr encoding)
        TaskPool-->>LabelMgr: QR strings
        LabelMgr-->>LoadingPopup: Result<Vec<String>>
    and Conditional Popup (After 125ms)
        LoadingPopup->>LoadingPopup: sleep(125ms)
        LoadingPopup->>Frontend: Updater::send_update(ShowLoadingPopup)
        Frontend->>UI: Display loading popup
    end
    
    alt Popup was shown
        LoadingPopup->>LoadingPopup: Ensure minimum 350ms display
        LoadingPopup->>Frontend: Updater::send_update(HideLoadingPopup)
        Frontend->>UI: Dismiss popup
    end
    
    LoadingPopup-->>Rust: Result<Vec<String>>
    Rust-->>UI: QR code strings
    UI->>UI: Render animated QR codes

    Note over UI,Frontend: Export Transactions CSV Flow

    UI->>Rust: exportTransactionsCsv()
    Rust->>LoadingPopup: with_loading_popup(async { ... })
    LoadingPopup->>Rust: actor.txns_with_prices()
    Rust->>TaskPool: spawn_blocking(create CSV)
    TaskPool-->>Rust: CSV content + filename
    
    alt Operation > 125ms
        LoadingPopup->>Frontend: ShowLoadingPopup
        Frontend->>UI: Display loading
        Note over LoadingPopup: Ensure 350ms minimum display
        LoadingPopup->>Frontend: HideLoadingPopup
        Frontend->>UI: Dismiss loading
    end
    
    Rust-->>UI: TransactionExportResult
    UI->>UI: Present share sheet
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Additional Comments (2)

  1. ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift, line 298-299 (link)

    logic: QR export not using new loading popup method - should call manager.rust.exportLabelsForQr(density: density) instead of labelManager.exportToBbqrWithDensity

  2. android/app/src/main/java/org/bitcoinppl/cove/wallet/WalletSheets.kt, line 166 (link)

    logic: QR export not using new loading popup method - should call manager.rust.exportLabelsForQr(density) instead of labelManager.exportToBbqrWithDensity

12 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Copy link

@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: 4

🧹 Nitpick comments (5)
ios/Cove/AppManager.swift (1)

278-282: Consider the scope of popup dismissal.

The HideLoadingPopup case dismisses all popups via PopupStack.dismissAllPopups(). If there are scenarios where multiple popups might be shown simultaneously, this could inadvertently dismiss unrelated popups.

💡 Consider a more targeted dismissal approach

If the loading popup can be tracked, dismissing specifically that popup would be safer:

case .showLoadingPopup:
    Task { 
        loadingPopup = await MiddlePopup(state: .loading).present() 
    }

case .hideLoadingPopup:
    Task { 
        await loadingPopup?.dismiss()
        loadingPopup = nil
    }

However, if the current approach is intentional and all popups should be cleared, this is fine.

android/app/src/main/java/org/bitcoinppl/cove/AppManager.kt (1)

580-591: Consider the scope and wording of the loading state.

The implementation sets alertState to a general alert for loading and clears it on hide. Two considerations:

  1. Text wording: "Working on it..." is informal. Consider if this matches the app's tone.
  2. State clearing: HideLoadingPopup sets alertState = null, which will clear any alert, not just the loading one. This could inadvertently dismiss error alerts if timing overlaps.
💡 Potential improvements

For more precise control, consider tracking the loading alert specifically:

is AppStateReconcileMessage.ShowLoadingPopup -> {
    val loadingAlert = TaggedItem(
        AppAlertState.General(
            title = "Please wait...",
            message = "",
        ),
    )
    if (alertState == null) {  // Only show if no other alert
        alertState = loadingAlert
    }
}

is AppStateReconcileMessage.HideLoadingPopup -> {
    // Only clear if it's the loading alert
    if (alertState?.item is AppAlertState.General) {
        alertState = null
    }
}

However, if the current approach is intentional and all alerts should be cleared, this is acceptable.

rust/src/loading_popup.rs (1)

23-34: The biased select correctly optimizes for fast operations.

The comment on line 23 explains the behavior, but consider adding a note in the function documentation about this optimization to make it more discoverable.

📝 Enhanced documentation
 /// Runs an async operation with conditional loading popup
-/// Shows popup only if operation takes >125ms, keeps visible for min 400ms
+/// Shows popup only if operation takes >125ms, keeps visible for min 400ms.
+/// The biased select! prioritizes checking operation completion first,
+/// which avoids showing the popup for operations that complete quickly.
 pub async fn with_loading_popup<F, T, E>(operation: F) -> Result<T, E>
android/app/src/main/java/org/bitcoinppl/cove/wallet/MoreInfoPopover.kt (1)

100-134: Consider more specific error handling.

The current error handling catches all exceptions generically. If the export methods can return null (as suggested by the ?. operator), that case is handled, but exceptions are broadly caught.

Consider providing more specific feedback based on the failure mode:

when (currentExportType) {
    is ExportType.Transactions -> {
        val result = currentManager?.rust?.exportTransactionsCsv()
        if (result == null) {
            snackbarHostState.showSnackbar("No transactions to export")
        } else {
            result.content
        }
    }
    // similar for labels...
}
ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift (1)

158-160: Consider removing the trivial wrapper.

The showQrExport() function is a simple one-line wrapper around a state mutation. Unless this is needed for consistency with other patterns in the codebase, direct state mutation on line 233 would be clearer.

💡 Simplification option
-    func showQrExport() {
-        showLabelsQrExport = true
-    }
-
     func shareLabelsFile() {
         // ...
     }

     // Later in the code:
     Button("QR Code") {
-        showQrExport()
+        showLabelsQrExport = true
     }

However, if the wrapper provides a clear semantic intent or is part of a larger pattern, keeping it is fine.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 70225af and 6bd6fbc.

⛔ Files ignored due to path filters (1)
  • ios/CoveCore/Sources/CoveCore/generated/cove.swift is excluded by !**/generated/**
📒 Files selected for processing (11)
  • android/app/src/main/java/org/bitcoinppl/cove/AppManager.kt (1 hunks)
  • android/app/src/main/java/org/bitcoinppl/cove/wallet/MoreInfoPopover.kt (1 hunks)
  • android/app/src/main/java/org/bitcoinppl/cove/wallet/WalletSheets.kt (2 hunks)
  • android/app/src/main/java/org/bitcoinppl/cove_core/cove.kt (12 hunks)
  • ios/Cove/AppManager.swift (2 hunks)
  • ios/Cove/Flows/SelectedWalletFlow/MoreInfoPopover.swift (1 hunks)
  • ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift (3 hunks)
  • rust/src/app/reconcile.rs (1 hunks)
  • rust/src/lib.rs (1 hunks)
  • rust/src/loading_popup.rs (1 hunks)
  • rust/src/manager/wallet_manager.rs (4 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
rust/**/*.rs

⚙️ CodeRabbit configuration file

rust/**/*.rs: - Verify correct functionality and logic consistency.

  • Check for idiomatic Rust usage and performance best practices.
  • Suggest improvements to error handling, unwraps, and Result handling.
  • Check for any potential memory leaks or unsafe code.
  • Check for any potential mutex deadlocks.
  • Check for potential security issues, make sure Bitcoin wallets are handled securely.
  • Identify spelling mistakes in comments, string literals, and documentation

Files:

  • rust/src/app/reconcile.rs
  • rust/src/loading_popup.rs
  • rust/src/lib.rs
  • rust/src/manager/wallet_manager.rs
android/app/src/main/java/org/bitcoinppl/cove/**/*.kt

⚙️ CodeRabbit configuration file

android/app/src/main/java/org/bitcoinppl/cove/**/*.kt: ⚠️ CRITICAL FFI/Threading Policy - READ FIRST:

  • NEVER suggest moving Rust FFI calls to background threads (withContext(Dispatchers.IO))

  • Rust FFI calls are SYNCHRONOUS and FAST - they complete in microseconds

  • The Rust core uses Tokio runtime internally and handles all async operations

  • Database operations (redb) are optimized and do NOT block the UI thread

  • ONLY suggest Dispatchers.IO with profiling evidence showing >16ms UI blocking

  • If you see a Rust FFI call on the main thread, DO NOT FLAG IT - this is correct

  • Ignore generated bindings code in android/app/src/main/java/org/bitcoinppl/cove_core/**

  • Verify correct functionality and logic consistency.

  • Check for idiomatic Kotlin usage and performance best practices.

FFI Resource Management:

  • Always verify that UniFFI objects implementing AutoCloseable call .close() before being nulled
  • Example: Mnemonic must call .close() to trigger zeroization of sensitive data
  • Use DisposableEffect for cleanup in Compose, never just null references

Compose Best Practices:

  • LaunchedEffect should be keyed to actual dependencies, not Unit
  • Set loading states at the beginning of LaunchedEffect blocks

Files:

  • android/app/src/main/java/org/bitcoinppl/cove/wallet/MoreInfoPopover.kt
  • android/app/src/main/java/org/bitcoinppl/cove/AppManager.kt
  • android/app/src/main/java/org/bitcoinppl/cove/wallet/WalletSheets.kt
ios/Cove/**/*.swift

⚙️ CodeRabbit configuration file

ios/Cove/**/*.swift: - Review SwiftUI view code for proper layout, best practices

  • Identify spelling mistakes in comments, string literals, and documentation
  • Ignore generated bindings code in ios/CoveCore/Sources/CoveCore/generated/**

Files:

  • ios/Cove/AppManager.swift
  • ios/Cove/Flows/SelectedWalletFlow/MoreInfoPopover.swift
  • ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift
🧬 Code graph analysis (5)
rust/src/loading_popup.rs (3)
ios/Cove/AppManager.swift (1)
  • reconcile (213-285)
android/app/src/main/java/org/bitcoinppl/cove/AppManager.kt (1)
  • reconcile (506-594)
rust/src/app/reconcile.rs (2)
  • reconcile (69-69)
  • send_update (61-63)
ios/Cove/AppManager.swift (2)
ios/Cove/Flows/SettingsFlow/SettingsScreen/NodeSelectionView.swift (1)
  • showLoadingPopup (42-49)
ios/CoveCore/Sources/CoveCore/generated/cove.swift (2)
  • state (3056-3062)
  • state (11097-11103)
rust/src/manager/wallet_manager.rs (2)
rust/src/loading_popup.rs (1)
  • with_loading_popup (14-47)
rust/src/label_manager.rs (1)
  • export_default_file_name (88-95)
ios/Cove/Flows/SelectedWalletFlow/MoreInfoPopover.swift (1)
ios/Cove/ShareSheet.swift (2)
  • present (38-70)
  • present (77-139)
ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift (2)
ios/CoveCore/Sources/CoveCore/generated/cove.swift (2)
  • exportLabelsForShare (7985-8000)
  • data (10119-10125)
ios/Cove/ShareSheet.swift (2)
  • present (38-70)
  • present (77-139)
⏰ 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). (5)
  • GitHub Check: Greptile Review
  • GitHub Check: test (ubuntu-latest)
  • GitHub Check: compile-ios
  • GitHub Check: test (macos-latest)
  • GitHub Check: compile-android
🔇 Additional comments (15)
rust/src/lib.rs (1)

18-18: LGTM!

The module declaration follows Rust naming conventions and is appropriately positioned in the module list.

ios/Cove/AppManager.swift (1)

1-1: LGTM!

The import is necessary for the popup functionality added in this PR.

rust/src/app/reconcile.rs (1)

34-35: LGTM!

The new enum variants are correctly defined and follow the existing pattern for reconcile messages.

android/app/src/main/java/org/bitcoinppl/cove/wallet/MoreInfoPopover.kt (1)

100-110: Excellent simplification - FFI calls correctly remain on the main thread.

The export flow now delegates to the new Rust methods that handle loading popup timing internally. The code correctly keeps FFI calls (exportTransactionsCsv(), exportLabelsForShare()) on the main thread and only moves file I/O to Dispatchers.IO.

Based on coding guidelines: Rust FFI calls are synchronous and fast, with async operations handled internally by the Tokio runtime.

ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift (1)

162-178: LGTM! Clean integration with the new export API.

The refactored shareLabelsFile() properly uses async/await to call the new Rust export method and includes error handling with user feedback.

android/app/src/main/java/org/bitcoinppl/cove/wallet/WalletSheets.kt (2)

122-131: Proper resource cleanup before async operations.

The exportLabelManager is correctly closed and nulled before launching the async share operation, ensuring resources are released promptly.


237-262: LGTM! Correct FFI usage and I/O dispatching.

The refactored function correctly:

  • Calls the Rust FFI method on the main thread (as it should be)
  • Moves only the file I/O operations to Dispatchers.IO
  • Uses the simplified export API that returns a ready-to-use result

Based on coding guidelines: Rust FFI calls should remain on the main thread, with only actual I/O operations dispatched to background threads.

ios/Cove/Flows/SelectedWalletFlow/MoreInfoPopover.swift (1)

40-56: LGTM! Clean simplification of export flow.

The refactored implementation correctly delegates the export logic to Rust (which handles the loading popup) and uses the standard ShareSheet pattern for sharing. Error handling is appropriate with clear user feedback.

rust/src/manager/wallet_manager.rs (3)

123-133: LGTM! Clean data structures for export results.

The new LabelExportResult and TransactionExportResult structs are properly defined with uniffi annotations for FFI compatibility.


402-414: LGTM! Proper async implementation with loading popup integration.

The method correctly:

  • Clones necessary data before the async block
  • Releases the RwLock immediately to avoid holding locks across await points
  • Delegates to the with_loading_popup helper for consistent UX
  • Uses proper error propagation with the ? operator

416-425: LGTM! Clean implementation.

Straightforward delegation to the label manager with proper loading popup integration.

android/app/src/main/java/org/bitcoinppl/cove_core/cove.kt (4)

1434-1439: LGTM: Standard UniFFI FFI declarations and checksum validation.

The external function declarations and checksum validations follow the expected UniFFI patterns. The runtime checksum validation will catch any mismatches between the Kotlin bindings and Rust implementation.

Also applies to: 2380-2385, 3820-3828


17988-18001: LGTM: Clear interface definitions with proper documentation.

The interface declarations are well-documented and the method signatures are appropriate for their export functionality.


24843-24877: LGTM: Well-structured data classes with proper FFI converters.

Both LabelExportResult and TransactionExportResult follow the same clean structure and have properly implemented FFI converters for serialization/deserialization across the Rust-Kotlin boundary.

Also applies to: 25423-25457


26421-26424: LGTM: Proper enum variant additions for loading popup state.

The two new AppStateReconcileMessage variants (ShowLoadingPopup and HideLoadingPopup) are correctly implemented across all required FFI methods (destroy, read, allocationSize, write) following the established pattern for unit variants.

Also applies to: 26509-26512, 26564-26565, 26660-26671, 26739-26746

Copy link

@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: 0

🧹 Nitpick comments (1)
ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift (1)

293-305: Consider using the new Rust API for consistency.

Line 298 still directly calls labelManager.exportToBbqrWithDensity(density: density), while the share flow (line 164) now uses the new manager.rust.exportLabelsForShare() API.

The Rust backend provides exportLabelsForQr(density:) with loading popup support. Consider updating line 298 to use this new API for consistency and to benefit from the loading popup during QR generation:

🔎 Optional refactor
 generateBbqrStrings: { density in
-    try labelManager.exportToBbqrWithDensity(density: density)
+    try await manager.rust.exportLabelsForQr(density: density)
 },

Note: This would require making the closure async, which may need adjustments to the QrExportView component.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6bd6fbc and 04dedeb.

📒 Files selected for processing (3)
  • ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift (2 hunks)
  • rust/src/app/reconcile.rs (2 hunks)
  • rust/src/manager/wallet_manager.rs (4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
rust/**/*.rs

⚙️ CodeRabbit configuration file

rust/**/*.rs: - Verify correct functionality and logic consistency.

  • Check for idiomatic Rust usage and performance best practices.
  • Suggest improvements to error handling, unwraps, and Result handling.
  • Check for any potential memory leaks or unsafe code.
  • Check for any potential mutex deadlocks.
  • Check for potential security issues, make sure Bitcoin wallets are handled securely.
  • Identify spelling mistakes in comments, string literals, and documentation

Files:

  • rust/src/app/reconcile.rs
  • rust/src/manager/wallet_manager.rs
ios/Cove/**/*.swift

⚙️ CodeRabbit configuration file

ios/Cove/**/*.swift: - Review SwiftUI view code for proper layout, best practices

  • Identify spelling mistakes in comments, string literals, and documentation
  • Ignore generated bindings code in ios/CoveCore/Sources/CoveCore/generated/**

Files:

  • ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift
🧬 Code graph analysis (3)
rust/src/app/reconcile.rs (1)
rust/src/app.rs (2)
  • global (121-123)
  • global (261-263)
rust/src/manager/wallet_manager.rs (2)
rust/src/loading_popup.rs (1)
  • with_loading_popup (14-47)
rust/src/label_manager.rs (1)
  • export_default_file_name (88-95)
ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift (3)
android/app/src/main/java/org/bitcoinppl/cove/wallet/WalletSheets.kt (1)
  • shareLabelsFile (237-263)
ios/CoveCore/Sources/CoveCore/generated/cove.swift (2)
  • exportLabelsForShare (7985-8000)
  • data (10119-10125)
ios/Cove/ShareSheet.swift (2)
  • present (38-70)
  • present (77-139)
🪛 GitHub Check: rustfmt
rust/src/manager/wallet_manager.rs

[warning] 446-446:
Diff in /home/runner/work/cove/cove/rust/src/manager/wallet_manager.rs

⏰ 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). (6)
  • GitHub Check: compile-ios
  • GitHub Check: clippy
  • GitHub Check: test (macos-latest)
  • GitHub Check: test (ubuntu-latest)
  • GitHub Check: ktlint
  • GitHub Check: compile-android
🔇 Additional comments (8)
rust/src/app/reconcile.rs (2)

34-35: LGTM! Clean enum variants for loading popup control.

The new ShowLoadingPopup and HideLoadingPopup variants integrate well with the loading popup mechanism and enable coordinated UI feedback across the frontend.


62-64: LGTM! Improved error handling for frontend disconnection.

Logging the error instead of panicking is the correct approach when the frontend disconnects. This prevents the backend from crashing and maintains service availability.

rust/src/manager/wallet_manager.rs (4)

123-133: LGTM! Well-structured export result types.

The LabelExportResult and TransactionExportResult structs are clean and properly annotated for FFI export, providing a clear interface for sharing export content and filenames across the language boundary.


402-414: LGTM! Clean implementation with loading popup support.

The method correctly integrates the loading popup mechanism and exports labels with appropriate filename generation.


416-425: LGTM! Proper QR export with loading popup.

The method appropriately wraps the QR export operation with the loading popup for better UX during potentially slow operations.


427-459: LGTM! Excellent improvements addressing previous review concerns.

This implementation successfully addresses both past review comments:

  1. Fixed error handling (lines 434-437): Replaced the unsafe double .unwrap().unwrap() with proper error mapping using map_err, converting both the actor call failure and the inner result error into appropriate error types.

  2. Implemented filename sanitization (lines 444-453): The wallet name is now properly sanitized before use in the filename, removing invalid filesystem characters and providing a safe fallback, matching the pattern used in label_manager.rs.

The loading popup integration is correct, and the overall implementation is robust.

Note: There's a minor rustfmt formatting hint on line 446, but this will be automatically resolved by running cargo fmt.

ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift (2)

157-159: LGTM! Good refactoring for code reusability.

The extracted showQrExport() helper method improves code consistency and makes the intent clearer.


161-177: LGTM! Excellent integration with the new Rust export API.

The updated implementation properly:

  • Uses async/await for the new exportLabelsForShare() API
  • Handles errors gracefully with user-friendly alerts
  • Leverages the loading popup support from the Rust backend
  • Simplifies the code by removing manual filename construction

@praveenperera praveenperera force-pushed the rust-conditional-loading-popup branch from 04dedeb to 2687ac9 Compare December 20, 2025 19:57
Copy link

@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: 0

🧹 Nitpick comments (1)
rust/src/manager/wallet_manager.rs (1)

429-456: Excellent error handling and filename sanitization.

The implementation properly addresses previous review concerns:

  • Lines 434-437: Two-level error handling correctly maps both actor communication errors and txns_with_prices() errors to appropriate Error variants (no more double unwrap)
  • Lines 444-450: Robust filename sanitization replaces spaces with underscores, removes problematic characters while preserving underscores, and provides a safe fallback

The loading popup wrapper ensures good UX for this potentially long-running operation.

💡 Optional: Extract filename sanitization to shared helper

The sanitization logic at lines 444-450 differs slightly from the similar logic in label_manager.rs (lines 87-94). Consider extracting this to a shared helper function to ensure consistency and reduce duplication:

// In a shared utility module
pub fn sanitize_for_filename(name: String, fallback: &str) -> String {
    let sanitized = name
        .replace(' ', "_")
        .replace(|c: char| !c.is_alphanumeric() && c != '_', "")
        .to_ascii_lowercase();
    
    if sanitized.is_empty() { fallback.to_string() } else { sanitized }
}

Then both locations could use: sanitize_for_filename(name, "wallet") or sanitize_for_filename(name, "labels").

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 04dedeb and 2687ac9.

📒 Files selected for processing (4)
  • android/app/src/main/java/org/bitcoinppl/cove/AppManager.kt (1 hunks)
  • ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift (2 hunks)
  • rust/src/app/reconcile.rs (2 hunks)
  • rust/src/manager/wallet_manager.rs (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • android/app/src/main/java/org/bitcoinppl/cove/AppManager.kt
  • rust/src/app/reconcile.rs
  • ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift
🧰 Additional context used
📓 Path-based instructions (1)
rust/**/*.rs

⚙️ CodeRabbit configuration file

rust/**/*.rs: - Verify correct functionality and logic consistency.

  • Check for idiomatic Rust usage and performance best practices.
  • Suggest improvements to error handling, unwraps, and Result handling.
  • Check for any potential memory leaks or unsafe code.
  • Check for any potential mutex deadlocks.
  • Check for potential security issues, make sure Bitcoin wallets are handled securely.
  • Identify spelling mistakes in comments, string literals, and documentation

Files:

  • rust/src/manager/wallet_manager.rs
🧬 Code graph analysis (1)
rust/src/manager/wallet_manager.rs (3)
rust/src/loading_popup.rs (1)
  • with_loading_popup (14-47)
rust/src/label_manager.rs (1)
  • export_default_file_name (88-95)
rust/src/manager/wallet_manager/actor.rs (2)
  • txns_with_prices (810-832)
  • new (160-176)
⏰ 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). (6)
  • GitHub Check: compile-ios
  • GitHub Check: test (ubuntu-latest)
  • GitHub Check: ktlint
  • GitHub Check: clippy
  • GitHub Check: test (macos-latest)
  • GitHub Check: compile-android
🔇 Additional comments (3)
rust/src/manager/wallet_manager.rs (3)

27-28: LGTM: Clean additions for export functionality.

The new imports and data structures are well-organized:

  • LabelManager and LabelManagerError enable label export operations
  • with_loading_popup provides conditional loading UI feedback
  • LabelExportResult and TransactionExportResult properly encapsulate export outcomes with content and filename
  • All types are correctly marked with uniffi attributes for FFI boundary

Also applies to: 50-50, 123-133


402-414: LGTM: Proper async pattern with loading popup.

The implementation correctly:

  • Clones data before moving into the async block (avoiding lock issues)
  • Uses with_loading_popup to show UI feedback only if the operation takes longer than the threshold
  • Propagates errors using ? operator
  • Delegates filename generation to the label manager's existing logic

418-425: LGTM: Clean QR export implementation.

Follows the same solid pattern as the share export method with appropriate loading popup wrapper and proper error handling.

Copy link

@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: 0

🧹 Nitpick comments (3)
android/app/src/main/java/org/bitcoinppl/cove/views/QrExportView.kt (1)

68-82: Set loading state at the beginning of LaunchedEffect.

Per Compose best practices, clear qrStrings at the start so users see "Loading..." during format/density changes instead of stale QR codes.

🔎 Proposed fix
 // generate QR codes on initial load and when format/density changes
 LaunchedEffect(selectedFormat, density) {
+    qrStrings = emptyList()
     try {
         qrStrings =
             when (selectedFormat) {
                 QrExportFormat.BBQR -> generateBbqrStrings(density)
                 QrExportFormat.UR -> generateUrStrings?.invoke(density) ?: generateBbqrStrings(density)
             }
         error = null
         currentIndex = 0
     } catch (e: Exception) {
         error = e.message ?: "Unknown error"
-        qrStrings = emptyList()
     }
 }

As per coding guidelines: "Set loading states at the beginning of LaunchedEffect blocks."

ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift (1)

157-159: Consider inlining the single-statement helper.

The showQrExport() method only sets a boolean flag. While it provides semantic clarity for the button action, you might consider inlining this directly in the button action for brevity, similar to how showReceiveSheet() is used. However, keeping it as a separate function is also fine if you anticipate adding additional logic later.

rust/src/manager/wallet_manager.rs (1)

391-400: Pre-existing issue: Double unwrap pattern.

While not introduced by this PR, the create_transactions_with_fiat_export method at line 394 still uses .unwrap().unwrap() which can panic. Consider refactoring this in a follow-up to match the improved error handling pattern used in the new export_transactions_csv method (lines 434-437).

🔎 Suggested improvement
     pub async fn create_transactions_with_fiat_export(&self) -> Result<String, Error> {
         let fiat_currency = Database::global().global_config.fiat_currency().unwrap_or_default();

-        let txns_with_prices = call!(self.actor.txns_with_prices()).await.unwrap().unwrap();
+        let txns_with_prices = call!(self.actor.txns_with_prices())
+            .await
+            .map_err(|e| Error::TransactionsRetrievalError(e.to_string()))?
+            .map_err(|e| Error::GetHistoricalPricesError(e.to_string()))?;

         let report = HistoricalFiatPriceReport::new(fiat_currency, txns_with_prices);
         let csv = report.create_csv().map_err_str(Error::CsvCreationError)?;

         Ok(csv.into_string())
     }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2687ac9 and 3a60032.

⛔ Files ignored due to path filters (1)
  • ios/CoveCore/Sources/CoveCore/generated/cove.swift is excluded by !**/generated/**
📒 Files selected for processing (7)
  • android/app/src/main/java/org/bitcoinppl/cove/views/QrExportView.kt (2 hunks)
  • ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift (3 hunks)
  • ios/Cove/Views/QrExportView.swift (3 hunks)
  • rust/src/label_manager.rs (2 hunks)
  • rust/src/loading_popup.rs (1 hunks)
  • rust/src/manager/wallet_manager.rs (4 hunks)
  • rust/src/task.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • rust/src/loading_popup.rs
🧰 Additional context used
📓 Path-based instructions (3)
ios/Cove/**/*.swift

⚙️ CodeRabbit configuration file

ios/Cove/**/*.swift: - Review SwiftUI view code for proper layout, best practices

  • Identify spelling mistakes in comments, string literals, and documentation
  • Ignore generated bindings code in ios/CoveCore/Sources/CoveCore/generated/**

Files:

  • ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift
  • ios/Cove/Views/QrExportView.swift
rust/**/*.rs

⚙️ CodeRabbit configuration file

rust/**/*.rs: - Verify correct functionality and logic consistency.

  • Check for idiomatic Rust usage and performance best practices.
  • Suggest improvements to error handling, unwraps, and Result handling.
  • Check for any potential memory leaks or unsafe code.
  • Check for any potential mutex deadlocks.
  • Check for potential security issues, make sure Bitcoin wallets are handled securely.
  • Identify spelling mistakes in comments, string literals, and documentation

Files:

  • rust/src/label_manager.rs
  • rust/src/task.rs
  • rust/src/manager/wallet_manager.rs
android/app/src/main/java/org/bitcoinppl/cove/**/*.kt

⚙️ CodeRabbit configuration file

android/app/src/main/java/org/bitcoinppl/cove/**/*.kt: ⚠️ CRITICAL FFI/Threading Policy - READ FIRST:

  • NEVER suggest moving Rust FFI calls to background threads (withContext(Dispatchers.IO))

  • Rust FFI calls are SYNCHRONOUS and FAST - they complete in microseconds

  • The Rust core uses Tokio runtime internally and handles all async operations

  • Database operations (redb) are optimized and do NOT block the UI thread

  • ONLY suggest Dispatchers.IO with profiling evidence showing >16ms UI blocking

  • If you see a Rust FFI call on the main thread, DO NOT FLAG IT - this is correct

  • Ignore generated bindings code in android/app/src/main/java/org/bitcoinppl/cove_core/**

  • Verify correct functionality and logic consistency.

  • Check for idiomatic Kotlin usage and performance best practices.

FFI Resource Management:

  • Always verify that UniFFI objects implementing AutoCloseable call .close() before being nulled
  • Example: Mnemonic must call .close() to trigger zeroization of sensitive data
  • Use DisposableEffect for cleanup in Compose, never just null references

Compose Best Practices:

  • LaunchedEffect should be keyed to actual dependencies, not Unit
  • Set loading states at the beginning of LaunchedEffect blocks

Files:

  • android/app/src/main/java/org/bitcoinppl/cove/views/QrExportView.kt
🧬 Code graph analysis (3)
ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift (3)
ios/CoveCore/Sources/CoveCore/generated/cove.swift (3)
  • exportLabelsForShare (8002-8017)
  • data (10136-10142)
  • exportToBbqrWithDensity (4781-4796)
ios/Cove/ShareSheet.swift (2)
  • present (38-70)
  • present (77-139)
ios/CoveCore/Sources/CoveCore/generated/cove_nfc.swift (1)
  • data (1081-1087)
rust/src/label_manager.rs (1)
rust/src/task.rs (1)
  • spawn_blocking (50-56)
rust/src/manager/wallet_manager.rs (2)
rust/src/loading_popup.rs (1)
  • with_loading_popup (13-46)
rust/src/label_manager.rs (1)
  • export_default_file_name (88-95)
⏰ 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). (6)
  • GitHub Check: clippy
  • GitHub Check: test (ubuntu-latest)
  • GitHub Check: test (macos-latest)
  • GitHub Check: compile-android
  • GitHub Check: ktlint
  • GitHub Check: compile-ios
🔇 Additional comments (14)
android/app/src/main/java/org/bitcoinppl/cove/views/QrExportView.kt (2)

55-56: LGTM!

The signature change to suspend function types is well-designed, enabling callers to provide async QR generation while maintaining backward compatibility with synchronous lambdas.


361-367: LGTM!

The convenience overload correctly passes FFI-backed lambdas to the suspend function parameters. Per coding guidelines, Rust FFI calls are synchronous and fast, so no threading changes are needed.

ios/Cove/Flows/SelectedWalletFlow/SelectedWalletScreen.swift (2)

161-177: LGTM!

The async export flow properly awaits the Rust API, destructures the result, and handles errors by presenting an alert. The error message includes the localized description for user-friendly feedback.


293-305: LGTM!

The QR export sheet correctly uses the updated async closure signature for generateBbqrStrings, properly awaiting the exportToBbqrWithDensity call.

rust/src/task.rs (1)

50-56: LGTM!

The spawn_blocking wrapper correctly delegates to Tokio's blocking executor with appropriate trait bounds. The expect() panic on uninitialized runtime is consistent with the existing spawn() and block_on() patterns in this module.

ios/Cove/Views/QrExportView.swift (3)

66-74: LGTM!

The async adoption is well-structured. Using .task for initial generation and wrapping onChange handlers in Task { await ... } blocks is the correct SwiftUI pattern for triggering async work from synchronous view lifecycle events.


213-223: LGTM!

The convenience initializer correctly provides backwards compatibility. The closures contain synchronous throwing calls that are valid within an async throws closure context since synchronous code executes normally in an async function.


188-208: LGTM!

The generateQrCodes() function properly handles async operations with appropriate error handling that updates UI state on failure.

rust/src/label_manager.rs (2)

242-255: LGTM!

The export() method correctly offloads database I/O to a blocking task. The db.clone() before the move is necessary for the 'static closure requirement, and the nested error handling properly maps both the inner Result and the outer JoinError.


258-293: LGTM!

The async/blocking separation is well-designed: the async export() runs first, then CPU-intensive BBQr encoding with Zlib compression is offloaded to a blocking task. The max_version extraction before the closure avoids borrow issues.

rust/src/manager/wallet_manager.rs (4)

123-133: LGTM!

The LabelExportResult and TransactionExportResult structs provide clean data containers for the export APIs, properly exposed via uniffi::Record for FFI.


402-414: LGTM!

The export_labels_for_share method correctly clones necessary data before the async move, wraps the operation in a loading popup for UX, and properly propagates errors.


416-425: LGTM!

Clean wrapper that adds loading popup behavior around the existing label manager method.


427-460: LGTM!

The export_transactions_csv method properly:

  • Uses with_loading_popup for UX
  • Handles actor call errors with descriptive error types (addressing prior review)
  • Offloads CPU-bound CSV generation to spawn_blocking
  • Sanitizes the wallet name for safe filenames (addressing prior review)
  • Falls back to "wallet" if sanitization produces an empty string

@praveenperera
Copy link
Contributor Author

@greptile-apps review pr

@greptile-apps
Copy link

greptile-apps bot commented Dec 20, 2025

Greptile's behavior is changing!

From now on, if a review finishes with no comments, we will not post an additional "statistics" comment to confirm that our review found nothing to comment on. However, you can confirm that we reviewed your changes in the status check section.

This feature can be toggled off in your Code Review Settings by deselecting "Create a status check for each PR".

@praveenperera praveenperera enabled auto-merge (squash) December 21, 2025 03:49
Copy link

@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: 0

🧹 Nitpick comments (1)
rust/src/loading_popup.rs (1)

13-46: Excellent async helper design with strong Rust practices.

The implementation demonstrates idiomatic async Rust:

  • Correct use of tokio::pin! for polling the future multiple times within select!
  • biased; prioritizes operation completion, avoiding unnecessary popup for fast operations
  • Proper minimum display duration prevents UI flicker
  • Generic Result<T, E> preserves the caller's error type
  • No unsafe code, unwraps, or memory concerns

Note: The concern in the previous review about Updater::send_update() panicking on channel closure reflects a misunderstanding. Tokio's mpsc send() method returns a Result on closure—it doesn't panic. If there's a need to guarantee delivery of UI updates, verify that the channel is created with sufficient capacity and the receiver is never dropped unexpectedly.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 811a04d and baad438.

📒 Files selected for processing (1)
  • rust/src/loading_popup.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
rust/**/*.rs

⚙️ CodeRabbit configuration file

rust/**/*.rs: - Verify correct functionality and logic consistency.

  • Check for idiomatic Rust usage and performance best practices.
  • Suggest improvements to error handling, unwraps, and Result handling.
  • Check for any potential memory leaks or unsafe code.
  • Check for any potential mutex deadlocks.
  • Check for potential security issues, make sure Bitcoin wallets are handled securely.
  • Identify spelling mistakes in comments, string literals, and documentation

Files:

  • rust/src/loading_popup.rs
🔇 Additional comments (1)
rust/src/loading_popup.rs (1)

9-10: Well-chosen timing constants for smooth UX.

The 50ms delay prevents popup flicker on fast operations, and the 350ms minimum display duration ensures the popup doesn't disappear too abruptly. These values provide good user experience without being overly conservative.

@praveenperera praveenperera merged commit 8a88eb8 into master Dec 21, 2025
9 checks passed
@praveenperera praveenperera deleted the rust-conditional-loading-popup branch December 21, 2025 04:00
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