Skip to content

UI: Tappable balance#163

Merged
danielnordh merged 17 commits intoreez:mainfrom
danielnordh:ux/mock
Feb 13, 2025
Merged

UI: Tappable balance#163
danielnordh merged 17 commits intoreez:mainfrom
danielnordh:ux/mock

Conversation

@danielnordh
Copy link
Copy Markdown
Collaborator

@danielnordh danielnordh commented Feb 11, 2025

This PR updates the way balances are shown.
A tappable view switches between the following states with primary and secondary values:

  • USD + total sats
  • USD + BTC
  • BTC + USD
  • Total sats + unit label
  • Onchain sats + unit label
  • Lightning sats + unit label
Simulator.Screen.Recording.-.iPhone.16.-.2025-02-11.at.13.13.31.mp4

Other changes:

  • Refactor BitcoinViewModel to use BalanceDetails (now exposed from LightningNodeClient) instead of multiple calls
  • Refactor BitcoinViewModel with a convenience update() function to use in BitcoinView
  • Add an overall AppMode(.live or .mock) so that we can switch in one place
  • Mock values for BalanceDetails
  • Mock values for transaction amounts

Summary by CodeRabbit

  • New Features

    • Unified balance details now display a consolidated view of on-chain and Lightning funds.
    • An interactive balance toggle lets users switch between different balance presentations for a personalized experience.
    • New functionality to asynchronously retrieve balance details from the Lightning node.
  • Refactor

    • Enhanced wallet configuration with adjustable modes for live and mock scenarios, ensuring consistent operational performance across the app.
    • Streamlined balance management logic for improved efficiency and clarity in balance retrieval.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 11, 2025

Walkthrough

This pull request updates the initialization of the WalletClient across the codebase by replacing the keyClient parameter with a new appMode parameter. The changes span from the core WalletClient class to various app views and services, including modifications to the balance retrieval logic in the Lightning node service and the Bitcoin view model. These updates standardize client configuration for both live and mock environments and consolidate balance management into unified structures.

Changes

File(s) Change Summary
LDKNodeMonday/App/LDKNodeMondayApp.swift
LDKNodeMonday/View/Home/NetworkSettingsView.swift
LDKNodeMonday/View/Home/OnboardingView.swift
LDKNodeMonday/View/Settings/SettingsView.swift
Updated WalletClient instantiation: replaced keyClient: ... with appMode: ... for both live and mock scenarios.
LDKNodeMonday/App/WalletClient.swift Modified initializer signature: changed from init(keyClient: KeyClient) to init(appMode: AppMode); added new public enum AppMode (with cases live and mock) and updated client property initialization logic.
LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift Introduced a new asynchronous method listBalances() that retrieves balance details via a call to ldkNode.listBalances(), and updated the LightningNodeClient closures along with corresponding mock implementations.
LDKNodeMonday/View Model/Home/BitcoinViewModel.swift Removed multiple individual balance properties and methods; added consolidated properties (balances, unifiedBalance, isBalancesFinished) and a new method getBalances() for balance retrieval.
LDKNodeMonday/View/Home/BitcoinView.swift Introduced a new state variable displayBalanceType and a BalanceHeader view; added a public enum DisplayBalanceType with cycling functionality; simplified refresh logic to call a unified update() method.

Sequence Diagram(s)

sequenceDiagram
    participant BV as BitcoinView
    participant BVM as BitcoinViewModel
    participant LNS as LightningNodeService
    BV->>BVM: getBalances()
    BVM->>LNS: listBalances() async call
    LNS-->>BVM: Return BalanceDetails
    BVM->>BV: Update balances & unifiedBalance
Loading

Possibly related PRs

  • ui: preview #124: The changes in the main PR regarding the initialization of the walletClient in LDKNodeMondayApp.swift are directly related to the modifications made in the WalletClient class in WalletClient.swift, as both involve the transition from using keyClient to appMode for instantiation.
  • Refactor: Save network and server into BackupInfo #154: The changes in the main PR, which involve modifying the initialization of the walletClient in LDKNodeMondayApp.swift, are related to the changes in the retrieved PR that also update the initialization of WalletClient in various files, indicating a direct connection in how the WalletClient is constructed.
  • UI - onboarding styling and colors #131: The changes in the main PR, specifically the modification of the walletClient initialization in LDKNodeMondayApp.swift, are directly related to the changes in the NetworkSettingsView.swift and OnboardingView.swift files in the retrieved PR, which also update the walletClient initialization to use the new appMode parameter.

Suggested reviewers

  • reez

Poem

I'm a rabbit with hops so bright,
Skipping through code in the pale moonlight.
Changing keys to modes with a quirky little dance,
Balances now unified—oh what a chance!
In our garden of code, every change is a delight!
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0fc7abf and 3176490.

📒 Files selected for processing (3)
  • LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift (4 hunks)
  • LDKNodeMonday/View Model/Home/BitcoinViewModel.swift (2 hunks)
  • LDKNodeMonday/View/Home/BitcoinView.swift (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • LDKNodeMonday/View/Home/BitcoinView.swift
🧰 Additional context used
🧠 Learnings (1)
LDKNodeMonday/View Model/Home/BitcoinViewModel.swift (1)
Learnt from: danielnordh
PR: reez/Monday#163
File: LDKNodeMonday/View Model/Home/BitcoinViewModel.swift:58-67
Timestamp: 2025-02-11T13:27:15.084Z
Learning: In LDKNodeMonday, the LightningNodeClient.balanceDetails() method is designed to be non-throwing and returns a BalanceDetails object asynchronously.
🔇 Additional comments (8)
LDKNodeMonday/View Model/Home/BitcoinViewModel.swift (4)

17-19: LGTM! Property consolidation improves code organization.

The consolidation of multiple balance properties into a single BalanceDetails object and unifiedBalance improves code organization and reduces state management complexity.


27-30: LGTM! Simplified USD value calculation.

The USD value calculation is now more straightforward using the consolidated unifiedBalance.


42-47: LGTM! Well-structured update sequence.

The update() method provides a clean, sequential way to refresh all necessary data, which aligns with the PR's goal of streamlining balance management.


58-67: LGTM! Thread-safe balance updates with proper concurrency handling.

The method correctly uses MainActor for UI updates and handles potential concurrency issues by creating a copy of the non-sendable balance object.

LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift (4)

176-179: LGTM! Clean implementation of balance retrieval.

The method provides a clean wrapper around the LDK node's balance functionality.


345-345: LGTM! Consistent client interface extension.

The addition of the balances closure property maintains consistency with the client's interface pattern.


446-502: LGTM! Comprehensive mock implementation.

The mock implementation provides realistic test data and covers all client functionality, which will be valuable for testing the new balance display features.


539-556: LGTM! Well-defined balance detail states.

The extension provides clear, well-defined states for empty and mock scenarios, which will be useful for testing and initialization.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@danielnordh danielnordh marked this pull request as ready for review February 11, 2025 13:21
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

🔭 Outside diff range comments (1)
LDKNodeMonday/App/WalletClient.swift (1)

53-53: ⚠️ Potential issue

Critical mismatch in start() ignoring appMode.
Even if appMode = .mock, line 53 still calls KeyClient.live.getBackupInfo(). Use the in-scope keyClient instead:

- backupInfo = try? KeyClient.live.getBackupInfo()
+ backupInfo = try? keyClient.getBackupInfo()
🧹 Nitpick comments (7)
LDKNodeMonday/View/Home/BitcoinView.swift (3)

40-40: Consider debouncing multiple update calls.

The view calls viewModel.update() in multiple places: refreshable, onAppear, onReceive, and various sheet dismissals. While consolidating to a single update() method is good, consider implementing debouncing to prevent rapid successive updates.

// Add to BitcoinViewModel
private var updateTask: Task<Void, Never>?

func debouncedUpdate() {
    updateTask?.cancel()
    updateTask = Task {
        try? await Task.sleep(nanoseconds: 500_000_000) // 500ms
        if !Task.isCancelled {
            await update()
        }
    }
}

Also applies to: 107-107, 123-123, 130-130, 177-177, 191-191, 218-218


230-308: Add documentation for the BalanceHeader component.

The component is well-structured, but would benefit from documentation explaining:

  • The purpose of each computed property
  • The display type cycle flow
  • The animation behavior

Add documentation like this:

+ /// Displays the wallet balance with configurable display types.
+ /// Supports cycling through different balance representations on tap.
 struct BalanceHeader: View {
     @Binding var displayBalanceType: DisplayBalanceType
     @ObservedObject var viewModel: BitcoinViewModel

+    /// The primary balance value based on the current display type.
+    /// - Returns: Formatted string representation of the balance.
     var balanceValue: String {
         // ...
     }

315-347: Add error handling for UserDefaults persistence.

The UserDefaults implementation could be more robust:

  1. Consider using a dedicated UserDefaults key enum
  2. Add error handling for invalid raw values
  3. Document the display type cycle order
private enum UserDefaultsKey {
    static let displayBalanceType = "displayBalanceType"
}

extension DisplayBalanceType {
    /// Cycles through display types in the following order:
    /// fiatSats -> fiatBtc -> btcFiat -> totalSats -> onchainSats -> lightningSats -> fiatSats
    mutating func next() {
        // ...
    }

    static let userDefaults: DisplayBalanceType = {
        guard let savedValue = UserDefaults.standard.string(forKey: UserDefaultsKey.displayBalanceType),
              let savedType = DisplayBalanceType(rawValue: savedValue) else {
            return .fiatBtc
        }
        return savedType
    }()
}
LDKNodeMonday/View Model/Home/BitcoinViewModel.swift (2)

42-47: Consider parallelizing the async calls.
All three async functions are invoked in sequence. You can potentially speed this up by using async let or a TaskGroup to run them concurrently, if the logic permits handling partial failures independently.


79-87: Consistent error propagation.
This mirrors the approach used throughout the file for setting bitcoinViewError on the main actor. Consider applying a similar do/catch in getBalanceDetails() for consistency.

LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift (2)

176-179: Consider adding error handling to balanceDetails().

While consistent with other methods in the class, consider adding error handling to make the method more robust. This is especially important as it's a new addition that could set a better pattern.

-func balanceDetails() async -> BalanceDetails {
-    let balanceDetails = ldkNode.listBalances()
-    return balanceDetails
+func balanceDetails() async throws -> BalanceDetails {
+    do {
+        let balanceDetails = ldkNode.listBalances()
+        return balanceDetails
+    } catch {
+        throw error
+    }
+}

539-556: Consider expanding mock BalanceDetails scenarios.

While the current mock implementation is good, consider adding more scenarios to test edge cases:

  • High balance values to test formatting
  • Non-empty lightningBalances array
  • Non-empty pendingBalancesFromChannelClosures
 static let mock = BalanceDetails(
     totalOnchainBalanceSats: 150000,
     spendableOnchainBalanceSats: 100000,
     totalAnchorChannelsReserveSats: 0,
     totalLightningBalanceSats: 50000,
-    lightningBalances: [],
-    pendingBalancesFromChannelClosures: []
+    lightningBalances: [
+        LightningBalance(channelId: "ch1", balanceMsat: 25000000),
+        LightningBalance(channelId: "ch2", balanceMsat: 25000000)
+    ],
+    pendingBalancesFromChannelClosures: [
+        PendingSweepBalance(txid: "tx1", confirmationHeight: 100, amountSats: 1000)
+    ]
 )
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e59dff5 and db55708.

📒 Files selected for processing (8)
  • LDKNodeMonday/App/LDKNodeMondayApp.swift (1 hunks)
  • LDKNodeMonday/App/WalletClient.swift (2 hunks)
  • LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift (4 hunks)
  • LDKNodeMonday/View Model/Home/BitcoinViewModel.swift (2 hunks)
  • LDKNodeMonday/View/Home/BitcoinView.swift (7 hunks)
  • LDKNodeMonday/View/Home/NetworkSettingsView.swift (1 hunks)
  • LDKNodeMonday/View/Home/OnboardingView.swift (1 hunks)
  • LDKNodeMonday/View/Settings/SettingsView.swift (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • LDKNodeMonday/View/Home/NetworkSettingsView.swift
  • LDKNodeMonday/View/Settings/SettingsView.swift
🧰 Additional context used
🧠 Learnings (1)
LDKNodeMonday/App/LDKNodeMondayApp.swift (1)
Learnt from: danielnordh
PR: reez/Monday#158
File: LDKNodeMonday/App/LDKNodeMondayApp.swift:47-52
Timestamp: 2025-01-29T19:03:13.205Z
Learning: Error handling for wallet operations (start, create, restart, delete) is managed within the WalletClient class, which updates both appError and appState properties when errors occur. Additional error handling in LDKNodeMondayApp is not needed.
🔇 Additional comments (11)
LDKNodeMonday/View/Home/BitcoinView.swift (2)

21-21: LGTM! Accessibility and state management improvements.

Good improvements:

  • Added state management for balance display types
  • Set maximum dynamic type size for better accessibility

Also applies to: 104-104


205-205: LGTM! Consistent use of balance details.

Good improvements:

  • Consistent usage of balanceDetails for spendable balance
  • Clean preview implementation with mock mode

Also applies to: 213-213, 353-353

LDKNodeMonday/View Model/Home/BitcoinViewModel.swift (4)

17-19: Good consolidation of balance properties.
Storing all related data in balanceDetails and having a concise unifiedBalance flag is a clean approach.


28-28: No immediate issues with the USD conversion.
Casting to Double and calling .valueInUSD(price:) is straightforward.


51-53: Handling non-sendable objects is correct.
Copying status into sCopy before updating on the main actor helps avoid concurrency issues.


72-77: UI state updates on the main actor look correct.
Ensuring that self.price and self.isPriceFinished are assigned on the main thread is appropriate for SwiftUI.

LDKNodeMonday/App/LDKNodeMondayApp.swift (1)

15-15: Switching to appMode aligns with the new WalletClient design.
This is consistent with the retrieved learning that additional error handling here is not needed.

LDKNodeMonday/App/WalletClient.swift (2)

22-31: Initialization with appMode is clear and flexible.
Distinguishing between .live and .mock helps maintain a cleaner design.


123-126: New AppMode enum neatly centralizes environment selection.
Explicitly specifying .live or .mock makes the code more testable.

LDKNodeMonday/View/Home/OnboardingView.swift (1)

141-141: LGTM: AppMode initialization aligns with the new configuration pattern.

The change from KeyClient.mock to AppMode.mock is consistent with the broader refactoring effort to standardize client configuration.

LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift (1)

446-502: LGTM: Well-structured mock implementation.

The mock implementation provides realistic test data and maintains consistency with the live implementation. The mock values are well-chosen for testing different scenarios.

Comment thread LDKNodeMonday/View Model/Home/BitcoinViewModel.swift Outdated
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: 0

🧹 Nitpick comments (1)
LDKNodeMonday/View/Home/BitcoinView.swift (1)

230-309: Consider adding documentation for the computed properties.

The BalanceHeader component is well-structured, but adding documentation for the computed properties would improve maintainability.

Add documentation comments for the computed properties:

+    /// Returns the primary balance value based on the current display type
     var balanceValue: String {
         // ... existing code ...
     }

+    /// Returns the secondary value to be displayed below the primary balance
     var secondaryValue: String {
         // ... existing code ...
     }

+    /// Returns the unit label based on the current display type
     var unitValue: String {
         // ... existing code ...
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between db55708 and 0fc7abf.

📒 Files selected for processing (1)
  • LDKNodeMonday/View/Home/BitcoinView.swift (7 hunks)
🔇 Additional comments (4)
LDKNodeMonday/View/Home/BitcoinView.swift (4)

21-21: LGTM! Good accessibility considerations.

The addition of displayBalanceType state and dynamic type size limit improves both functionality and accessibility.

Also applies to: 104-104


40-40: Improved efficiency by consolidating balance updates.

The consolidation of multiple balance retrieval calls into a single update() method reduces redundant API calls and improves performance.

Also applies to: 107-108, 123-124, 130-131, 177-178, 191-192, 218-219


316-348: LGTM! Well-structured enum with robust default handling.

The DisplayBalanceType implementation provides a clean way to manage different balance display states with proper persistence.


354-354: LGTM! Preview updated to use new AppMode.

The preview configuration correctly reflects the new WalletClient initialization pattern.

@reez
Copy link
Copy Markdown
Owner

reez commented Feb 13, 2025

ACK

just needs that one function renamed to listBalances before merge

@danielnordh danielnordh merged commit ece17c2 into reez:main Feb 13, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Feb 19, 2025
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