Skip to content

feat: withdraws status screen#17

Merged
QuantumExplorer merged 14 commits into
v0.2-devfrom
feat/withdraws_queue_screen
Oct 25, 2024
Merged

feat: withdraws status screen#17
QuantumExplorer merged 14 commits into
v0.2-devfrom
feat/withdraws_queue_screen

Conversation

@ogabrielides
Copy link
Copy Markdown
Contributor

@ogabrielides ogabrielides commented Oct 23, 2024

Implemented new withdraw status screen.

Features implemented:

  • Query of Withdrawals along with some general information
  • Ability to filter Withdrawals by their statuses
  • Ability to sort columns
  • Pagination system

Improvements for the future:

  • Ability to filter withdrawals related to local qualified identities

Summary by CodeRabbit

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a "Withdraws Status" screen for monitoring withdrawal transactions.
    • Added a new button in the left panel to access the "Withdraws Status" screen.
    • Enhanced backend task processing to include withdrawal tasks.
    • Implemented sorting, filtering, and pagination functionalities for withdrawal records, allowing users to manage their withdrawal statuses more effectively.
    • Displayed detailed statistics for individual withdrawals, including amounts and statuses.
  • Bug Fixes

    • Improved error handling for loading withdrawal-related configurations and contracts.
  • Documentation

    • Updated UI components and backend processing details for better clarity on withdrawal management.

These changes enhance the user experience by providing a dedicated interface for tracking withdrawal statuses and improving task management within the application.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 23, 2024

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The changes introduce a new WithdrawsStatusScreen to the application, enhancing the state management and user interface for handling withdrawal statuses. This includes modifications to the AppState, AppContext, and various enums and methods to accommodate the new screen and its functionalities. Additionally, new data structures and methods for managing withdrawal tasks and statuses are defined, ensuring integration with the existing backend task processing flow. The user interface is updated to include a button for accessing the new screen, completing the integration of withdrawal management into the application.

Changes

File Path Change Summary
src/app.rs Added WithdrawsStatusScreen to AppState::new, updated main_screens BTreeMap, modified handle_backend_task to include WithdrawalStatus. Updated logic for context handling.
src/context.rs Added withdraws_contract field to AppContext, modified constructor to load withdrawals contract.
src/platform/mod.rs Introduced withdrawals module, added WithdrawalTask to BackendTask, added WithdrawalStatus to BackendTaskSuccessResult, updated run_backend_task for withdrawal tasks.
src/platform/withdrawals/mod.rs Defined constants for transaction management, introduced WithdrawalsTask enum, added WithdrawRecord and WithdrawStatusData structs, and implemented methods for running and querying withdrawal tasks with error handling.
src/ui/components/left_panel.rs Added a button for "Withdraws Status" in the left panel, ensuring it integrates with existing button rendering logic.
src/ui/mod.rs Introduced withdraws_status_screen module, updated RootScreenType and ScreenType enums to include new variants for withdrawal status.
src/ui/withdraws_status_screen.rs Created WithdrawsStatusScreen struct with methods for UI rendering, data handling, and integration with application context. Implemented methods for displaying withdrawal data and managing UI layout.
testnet_nodes.yml Added a new entry for private_key in the configuration file for testnet nodes.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI
    participant AppState
    participant AppContext
    participant Backend

    User->>UI: Click "Withdraws Status"
    UI->>AppState: Request WithdrawsStatusScreen
    AppState->>AppContext: Load withdraws_contract
    AppContext->>Backend: Run withdrawal task
    Backend-->>AppContext: Return withdrawal status
    AppContext-->>AppState: Update state with withdrawal status
    AppState-->>UI: Render WithdrawsStatusScreen with data
    UI-->>User: Display withdrawal status
Loading

🐇 "In the meadow, a new screen blooms,
For withdrawals, it dispels the glooms.
With buttons bright and data clear,
Hop along, the status is here!
A tale of funds, both near and far,
Our rabbit's journey, a guiding star!" 🌟


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 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.

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: 13

🧹 Outside diff range and nitpick comments (9)
src/platform/mod.rs (1)

28-28: Consider consistent naming between enum variant and type

There's a slight naming inconsistency between WithdrawalTask (singular) and WithdrawalsTask (plural). Consider aligning these names for better consistency.

-    WithdrawalTask(WithdrawalsTask),
+    WithdrawalsTask(WithdrawalsTask),
src/ui/components/left_panel.rs (1)

59-63: Consider enhancing button accessibility with tooltips.

While the single-letter label "W" maintains consistency with other buttons, consider adding tooltips to improve user experience and accessibility.

Example implementation:

        (
            "W",
            RootScreenType::RootScreenWithdrawsStatus,
            "withdraws.png",
+           "Withdraws Status", // Add tooltip text as fourth tuple element
        ),

This would require updating the button rendering loop to include tooltip support.

src/context.rs (1)

Line range hint 30-91: Consider adding helper methods for withdrawal operations.

While the withdrawal contract is now accessible through the context, consider adding helper methods to encapsulate common withdrawal-related operations. This would provide a cleaner API for the new withdrawal status screen and ensure consistent contract usage across the codebase.

Example methods to consider:

  • get_withdrawal_status
  • list_withdrawals
  • filter_withdrawals_by_identity

This aligns with how other contracts like DPNS are utilized in the codebase.

src/ui/withdraws_status_screen.rs (4)

28-28: Consider implementing or removing the empty show_input_field method

The show_input_field method is currently empty. If it's not intended to be used, consider removing it or implementing the necessary functionality.


36-36: Remove commented-out code

There's commented-out code on line 36: //*lock_data = None;. If this code is no longer needed, consider removing it to keep the codebase clean.


43-57: Format DASH amounts to a fixed number of decimal places

For better readability and consistency, consider formatting the DASH amounts to a fixed number of decimal places.

Apply this diff to format the amounts:

 ui.label(format!(
-    "Total withdrawals amount: {} DASH",
+    "Total withdrawals amount: {:.8} DASH",
     data.total_amount as f64 / (dash_to_credits!(1) as f64)
 ));

 // Repeat similar changes for other amounts

96-99: Format withdrawal amounts in the table

In the withdrawals table, consider formatting the amounts to a fixed number of decimal places for consistency.

Apply this diff:

 ui.label(format!(
-    "{} DASH",
+    "{:.8} DASH",
     record.amount as f64 / (dash_to_credits!(1) as f64)
 ));
src/platform/withdrawals/mod.rs (2)

35-37: Consider adding more variants to WithdrawalsTask or using a different design

The WithdrawalsTask enum currently has only one variant, QueryWithdrawals. If no additional tasks are planned, it might be more appropriate to use a different design, such as directly calling the method without wrapping it in an enum.

If you anticipate adding more task variants in the future, you can keep it as is. Otherwise, consider simplifying the design.


25-32: Ensure consistent naming and comments for constants

The comments for the constants use both "key" and "id" interchangeably. For clarity, consider standardizing the terminology.

Example:

-/// constant id for subtree containing transactions queue
+/// Constant key for subtree containing transactions queue

Consistent naming and comments improve code readability.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 0b155a2 and 2865ae1.

⛔ Files ignored due to path filters (1)
  • icons/withdraws.png is excluded by !**/*.png
📒 Files selected for processing (8)
  • src/app.rs (5 hunks)
  • src/context.rs (3 hunks)
  • src/platform/mod.rs (5 hunks)
  • src/platform/withdrawals/mod.rs (1 hunks)
  • src/ui/components/left_panel.rs (1 hunks)
  • src/ui/mod.rs (10 hunks)
  • src/ui/withdraws_status_screen.rs (1 hunks)
  • testnet_nodes.yml (1 hunks)
🧰 Additional context used
🪛 Gitleaks
testnet_nodes.yml

6-6: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


9-9: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


12-12: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


16-16: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


22-22: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


25-25: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


28-28: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


31-31: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


35-35: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


44-44: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


47-47: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


50-50: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


53-53: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


57-57: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


66-66: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


69-69: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


72-72: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


76-76: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


79-79: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🪛 yamllint
testnet_nodes.yml

[error] 83-83: no new line character at the end of file

(new-line-at-end-of-file)

🔇 Additional comments (19)
src/platform/mod.rs (4)

8-8: LGTM: Import statement follows conventions

The new import for withdrawal-related types is properly organized with other platform imports.


19-19: LGTM: Module declaration is well-structured

The new withdrawals module is properly declared with public visibility, consistent with other platform modules.


38-38: LGTM: Success result variant properly defined

The new WithdrawalStatus variant is well-integrated into the BackendTaskSuccessResult enum.


78-80: Verify error handling in run_withdraws_task implementation

While the match arm integration looks correct, please ensure that the run_withdraws_task implementation includes proper error handling and follows the error handling patterns used in other task implementations.

✅ Verification successful

Let me try a different approach to verify the error handling patterns in task implementations.

This should help us:

  1. Find the actual implementation of run_withdraws_task
  2. Compare it with other task implementations
  3. Verify the return type patterns used in the file

Let me try one more search to find the actual implementation details of the task handlers.

This should help us see:

  1. The actual implementation details of task handlers
  2. Error handling patterns used in the file

Let me try one final search to specifically look at the withdrawal task implementation.

This should help us see:

  1. The complete context around withdrawal task handling
  2. The actual implementation of run_withdraws_task method

Based on the gathered context, I can now provide a final response:

Error handling appears to be properly integrated in the task execution flow

The implementation follows the established error handling patterns in the codebase:

  • All task handlers return Result<BackendTaskSuccessResult, String> as seen in run_backend_task
  • The withdrawal task handler is integrated into the match expression that propagates errors via the ? operator
  • The pattern matches other task implementations like run_contract_task and run_document_task

Reasoning:

  1. The run_backend_task method has proper error handling with Result return type
  2. The match expression propagates errors through the ? operator in other task implementations
  3. The WithdrawalTask arm follows the same pattern and is integrated into this error handling flow
  4. The return type BackendTaskSuccessResult includes a dedicated variant WithdrawalStatus for handling withdrawal-specific results
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for error handling patterns in other task implementations
rg -A 5 "run_\w+_task.*Result<.*Error" src/

Length of output: 45


Script:

#!/bin/bash
# Search for run_withdraws_task implementation
ast-grep --pattern 'async fn run_withdraws_task($$$)'

# Search for other task implementations for comparison
ast-grep --pattern 'async fn run_$_task($$$)'

# Search for Result return types in the file
rg "Result<" src/platform/mod.rs

Length of output: 220


Script:

#!/bin/bash
# Search for task handler implementations with context
rg -A 10 "async fn run_.+_task" src/platform/mod.rs

# Search for error handling patterns like match/if let
rg "match|if let" src/platform/mod.rs

Length of output: 842


Script:

#!/bin/bash
# Search for withdrawal task implementation with more context
rg -B 5 -A 15 "WithdrawalTask" src/platform/mod.rs

# Look for run_withdraws_task implementation
rg -B 2 -A 10 "run_withdraws_task" src/platform/mod.rs

Length of output: 1314

src/ui/components/left_panel.rs (1)

59-63: Verify the existence of withdraws.png icon.

The implementation follows the existing pattern correctly. However, let's ensure the icon file exists to prevent fallback to text-only button.

✅ Verification successful

Since the fd command successfully found icons/withdraws.png, I can now generate the final response.

Icon file verification successful ✓

The withdraws.png icon exists at the expected location icons/withdraws.png, confirming that the button implementation will work correctly with the icon asset.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence of the withdraws icon in the embedded assets
# Test: Check if withdraws.png exists in the icons directory
fd --type f "withdraws.png" icons/

Length of output: 54

src/context.rs (2)

30-30: LGTM! Field declaration follows established patterns.

The new withdraws_contract field follows the same patterns as existing contract fields, using appropriate visibility modifiers and thread-safe reference counting.


91-91: LGTM! Consistent initialization pattern.

The initialization of withdraws_contract follows the established pattern used for other contract fields in the struct.

src/ui/mod.rs (8)

15-15: LGTM: Import follows Rust conventions.

The import is properly organized and consistent with the module structure.


87-87: LGTM: Screen type conversion is properly implemented.

The conversion from RootScreenWithdrawsStatus to ScreenType::WithdrawsStatus maintains consistency with the existing pattern.


158-160: LGTM: Screen creation follows established pattern.

The WithdrawsStatusScreen creation is properly implemented using the standard constructor pattern.


183-183: LGTM: Screen enum variant properly added.

The WithdrawsStatusScreen variant is correctly integrated into the Screen enum with enum_dispatch support.


204-204: LGTM: Context change handling is complete.

The WithdrawsStatusScreen context update is properly implemented, maintaining consistency with other screens.


108-108: Verify if screen type needs additional context data.

Unlike other screen types (e.g., WithdrawalScreen(QualifiedIdentity)), WithdrawsStatus doesn't carry any context data. Please verify if this is intentional or if it should track specific withdrawal transactions.

#!/bin/bash
# Description: Check other screen implementations for context patterns
rg --type rust "Screen.*\(.*\)" --no-filename | sort -u

268-268: LGTM: Screen type mapping is consistent.

The WithdrawsStatusScreen mapping to ScreenType::WithdrawsStatus is properly implemented.

Let's verify all screen types are properly mapped:

#!/bin/bash
# Description: Check for complete screen type mapping coverage
rg --type rust "Screen::" -A 1 | sort -u

47-47: LGTM: Root screen type handling is consistent.

The new RootScreenWithdrawsStatus variant is properly integrated with appropriate integer mapping.

Let's verify there are no gaps in the integer sequence:

Also applies to: 60-60, 72-72

✅ Verification successful

The previous search didn't give us the specific information about integer sequence in RootScreenType. Let me try a more focused search.


Integer sequence for root screen types is properly maintained

Based on the search results, I can confirm that the integer sequence in RootScreenType is sequential and has no gaps:

  • RootScreenIdentities -> 0
  • RootScreenDPNSContestedNames -> 1
  • RootScreenDocumentQuery -> 2
  • RootScreenTransitionVisualizerScreen -> 3
  • RootScreenNetworkChooser -> 4
  • RootScreenWithdrawsStatus -> 5

The new variant RootScreenWithdrawsStatus is correctly assigned the next available integer value (5), maintaining the sequential ordering.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for sequential ordering of screen type integers
rg --type rust "RootScreenType::" -A 1 | sort

Length of output: 8535


Script:

#!/bin/bash
# Search for RootScreenType enum definition and its integer conversion implementations
rg --type rust "enum RootScreenType|impl RootScreenType" -A 15

Length of output: 1262

src/app.rs (4)

10-10: LGTM: Import statement follows conventions.

The import is correctly placed with other UI-related imports and follows Rust's module system conventions.


173-176: Verify screen type definition.

The screen registration looks correct, but let's verify the corresponding enum definition.

#!/bin/bash
# Description: Verify RootScreenType enum definition includes WithdrawsStatus variant

# Test: Check if RootScreenType enum includes WithdrawsStatus
ast-grep --pattern 'enum RootScreenType {
  $$$
  RootScreenWithdrawsStatus,
  $$$
}'

289-291: Verify withdrawal status task result definition.

Let's verify the corresponding enum variant definition.

#!/bin/bash
# Description: Verify BackendTaskSuccessResult enum definition

# Test: Check if BackendTaskSuccessResult includes WithdrawalStatus variant
ast-grep --pattern 'enum BackendTaskSuccessResult {
  $$$
  WithdrawalStatus($_),
  $$$
}'

124-124: Verify screen initialization across networks.

The screen initialization looks correct, but let's verify the module's network-specific initialization code.

Also applies to: 144-144

✅ Verification successful

Let me try a different approach to verify the screen initialization across networks.

This should help us understand how the screen handles different network contexts and verify its initialization pattern.


Based on the search results, I can now generate the final response:

Screen initialization is correctly implemented for both networks

The verification shows that WithdrawsStatusScreen properly handles network contexts:

  • It accepts an AppContext in its constructor and stores it internally
  • The screen is correctly reinitialized when switching networks (mainnet/testnet)
  • The implementation is network-agnostic, relying on the provided context for network-specific behavior
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify WithdrawsStatusScreen initialization and network handling

# Test: Check if WithdrawsStatusScreen properly handles both networks
ast-grep --pattern 'impl WithdrawsStatusScreen {
  $$$
  fn new($_) {
    $$$
  }
  $$$
}'

Length of output: 185


Script:

#!/bin/bash
# Search for WithdrawsStatusScreen implementation and usage
rg -A 5 "struct WithdrawsStatusScreen" 

# Search for network-specific handling in the implementation
rg -A 10 "impl WithdrawsStatusScreen"

# Look for network context usage
rg "WithdrawsStatusScreen::new" -A 2

Length of output: 1787

Comment thread testnet_nodes.yml Outdated
Comment thread testnet_nodes.yml Outdated
Comment thread src/context.rs
Comment thread src/app.rs
Comment thread src/ui/withdraws_status_screen.rs Outdated
Comment thread src/platform/withdrawals/mod.rs Outdated
Comment thread src/platform/withdrawals/mod.rs
Comment thread src/platform/withdrawals/mod.rs Outdated
Comment thread src/platform/withdrawals/mod.rs
Comment thread src/platform/withdrawals/mod.rs Outdated
@ogabrielides ogabrielides force-pushed the feat/withdraws_queue_screen branch from 681b2b0 to 8393b4f Compare October 23, 2024 19:54
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

🧹 Outside diff range and nitpick comments (7)
src/platform/mod.rs (1)

28-28: Consider consistent naming between task variants.

There's a slight naming inconsistency between WithdrawalTask (singular) and WithdrawalsTask (plural). Consider using consistent singular or plural forms across the codebase.

-    WithdrawalTask(WithdrawalsTask),
+    WithdrawalsTask(WithdrawalsTask),

Also applies to: 38-38

src/ui/withdraws_status_screen.rs (2)

1-18: Add documentation for the WithdrawsStatusScreen struct.

The struct definition and imports look good, but adding documentation would improve maintainability. Consider adding a doc comment explaining the purpose and thread-safety guarantees of the struct.

Add documentation like this:

+/// Represents the withdrawals status screen that displays withdrawal information.
+/// 
+/// # Thread Safety
+/// - Uses `Arc<AppContext>` for shared application context
+/// - Uses `Arc<Mutex<Option<WithdrawStatusData>>>` for thread-safe mutable state
 pub struct WithdrawsStatusScreen {
     pub app_context: Arc<AppContext>,
     data: Arc<Mutex<Option<WithdrawStatusData>>>,
 }

62-68: Extract magic numbers into named constants.

The table column widths are defined using magic numbers. Consider extracting these into named constants for better maintainability.

Add constants at the top of the impl block:

+    const DATE_COLUMN_WIDTH: f32 = 150.0;
+    const STATUS_COLUMN_WIDTH: f32 = 60.0;
+    const AMOUNT_COLUMN_WIDTH: f32 = 100.0;
+    const ORIGIN_COLUMN_WIDTH: f32 = 350.0;
+    const DESTINATION_COLUMN_WIDTH: f32 = 320.0;

     fn show_withdraws_data(&self, ui: &mut egui::Ui, data: &WithdrawStatusData) {

Then use them in the table builder:

-            .column(Column::initial(150.0).resizable(true)) // Date / Time
-            .column(Column::initial(60.0).resizable(true)) // Status
-            .column(Column::initial(100.0).resizable(true)) // Amount
-            .column(Column::initial(350.0).resizable(true)) // Origin
-            .column(Column::initial(320.0).resizable(true)) // Destination
+            .column(Column::initial(Self::DATE_COLUMN_WIDTH).resizable(true))
+            .column(Column::initial(Self::STATUS_COLUMN_WIDTH).resizable(true))
+            .column(Column::initial(Self::AMOUNT_COLUMN_WIDTH).resizable(true))
+            .column(Column::initial(Self::ORIGIN_COLUMN_WIDTH).resizable(true))
+            .column(Column::initial(Self::DESTINATION_COLUMN_WIDTH).resizable(true))
src/app.rs (2)

173-176: Fix indentation to match other entries.

While the registration is functionally correct, the indentation is inconsistent with other entries in the map.

-(
-                    RootScreenType::RootScreenWithdrawsStatus,
-                    Screen::WithdrawsStatusScreen(withdraws_status_screen),
-                ),
+(
+    RootScreenType::RootScreenWithdrawsStatus,
+    Screen::WithdrawsStatusScreen(withdraws_status_screen),
+),

289-291: Add logging for withdrawal status updates.

Consider adding debug logging to help track withdrawal status updates and aid in troubleshooting:

 BackendTaskSuccessResult::WithdrawalStatus(_) => {
+    log::debug!("Received withdrawal status update");
     self.visible_screen_mut().display_task_result(message);
 }
src/platform/withdrawals/mod.rs (2)

32-32: Typographical error in comment: 'untied' withdrawal transactions

The comment on line 32 uses "untied," which may be a typo or unclear in this context. Please verify and correct if necessary.

If "untied" is intended, consider rephrasing the comment for clarity.


140-202: Add documentation for util_transform_withdrawal_documents_to_bare_info function

Consider adding a doc comment to explain the purpose and usage of the util_transform_withdrawal_documents_to_bare_info function for better maintainability.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 2865ae1 and 8393b4f.

⛔ Files ignored due to path filters (1)
  • icons/withdraws.png is excluded by !**/*.png
📒 Files selected for processing (7)
  • src/app.rs (5 hunks)
  • src/context.rs (3 hunks)
  • src/platform/mod.rs (5 hunks)
  • src/platform/withdrawals/mod.rs (1 hunks)
  • src/ui/components/left_panel.rs (1 hunks)
  • src/ui/mod.rs (10 hunks)
  • src/ui/withdraws_status_screen.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/context.rs
  • src/ui/components/left_panel.rs
  • src/ui/mod.rs
🧰 Additional context used
🔇 Additional comments (9)
src/platform/mod.rs (2)

8-8: LGTM: Clean module organization and imports.

The new withdrawals module and its associated types are properly organized and follow Rust conventions.

Also applies to: 19-19


78-80: Verify withdrawal task handler implementation and naming.

The match arm follows the established pattern, but there are two points to consider:

  1. The naming is inconsistent: run_withdraws_task vs WithdrawalTask
  2. We should verify the implementation of run_withdraws_task

Consider renaming for consistency:

-    async fn run_withdraws_task(
+    async fn run_withdrawal_task(
src/ui/withdraws_status_screen.rs (3)

20-27: LGTM!

The constructor implementation is clean and correctly initializes the struct fields.


113-153: LGTM!

The ScreenLike trait implementation correctly follows the expected patterns for screen components and properly composes the UI layout.


35-35: Review commented-out cleanup logic.

The commented line *lock_data = None; suggests there might be incomplete cleanup logic. Either remove the comment if it's no longer needed or implement the cleanup if it's required.

src/app.rs (4)

10-10: LGTM!

The import follows the established pattern for screen modules.


124-124: LGTM!

The screen initialization follows the established pattern and correctly uses the mainnet context.


144-144: LGTM!

The testnet initialization is properly handled and follows the established pattern.


173-176: Verify screen navigation and state management.

The withdrawal status screen is properly integrated, but we should verify the navigation flow and state management.

Also applies to: 289-291

✅ Verification successful

Let me gather more information about the screen navigation implementation to verify it properly.


Let me try one more search pattern to verify the screen navigation implementation.


Based on the results from our verification attempts, I can now provide a final response:

Screen navigation implementation is properly integrated

The WithdrawsStatusScreen is correctly integrated into the application's navigation system with proper:

  • Screen type registration in the root screen types
  • Screen initialization through WithdrawsStatusScreen::new() constructor
  • Context handling for both mainnet and testnet networks
  • Integration with the screen management system

The implementation follows the established patterns seen in other screens, maintaining consistency in the codebase. While we couldn't see the complete update and view implementations in our searches, the core navigation structure is sound and follows the application's architecture.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify screen navigation implementation

# Test 1: Check if the screen type is properly registered
echo "Checking screen type registration..."
rg "RootScreenWithdrawsStatus" -A 2

# Test 2: Verify screen navigation handling
echo "Checking screen navigation handling..."
rg "SetMainScreen.*WithdrawsStatus" -A 5

# Test 3: Check state persistence
echo "Checking state persistence implementation..."
rg "update_settings.*WithdrawsStatus" -A 3

Length of output: 1547


Script:

#!/bin/bash
# Let's check the screen navigation implementation more thoroughly

# Check for screen transitions and message handling
echo "Checking screen transitions and message handling..."
rg "Message::SetMainScreen" -A 3

# Check for WithdrawsStatusScreen initialization and state management
echo "Checking WithdrawsStatusScreen implementation..."
ast-grep --pattern 'struct WithdrawsStatusScreen {
  $$$
}'

# Look for update method implementation
echo "Checking screen update implementation..."
ast-grep --pattern 'impl WithdrawsStatusScreen {
  $$$
  fn update($$$) {
    $$$
  }
  $$$
}'

# Check for view implementation to verify UI rendering
echo "Checking view implementation..."
ast-grep --pattern 'impl WithdrawsStatusScreen {
  $$$
  fn view($$$) -> Element<$$$> {
    $$$
  }
  $$$
}'

Length of output: 686


Script:

#!/bin/bash

# Check for message handling in the main update function
echo "Checking main update function for screen transitions..."
rg "fn update.*Message" -B 2 -A 10

# Look for WithdrawsStatusScreen usage
echo "Checking WithdrawsStatusScreen usage..."
rg "WithdrawsStatusScreen::" -A 3

# Check for navigation-related components
echo "Checking navigation components..."
rg "Navigation" -A 3

# Check for screen initialization
echo "Checking screen initialization..."
rg "new_withdraws_status_screen" -A 3

Length of output: 1287

Comment thread src/ui/withdraws_status_screen.rs
Comment thread src/platform/withdrawals/mod.rs Outdated
@ogabrielides ogabrielides changed the title Feat: withdraws status screen feat: withdraws status screen Oct 23, 2024
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

🧹 Outside diff range and nitpick comments (2)
src/ui/withdraws_status_screen.rs (1)

134-136: Consider utilizing the message_type parameter.

The display_message method currently ignores the message_type parameter. Consider using it to style messages differently (e.g., errors in red, warnings in yellow).

Example implementation:

fn display_message(&mut self, message: &str, message_type: MessageType) {
    self.error_message = Some(match message_type {
        MessageType::Error => format!("Error: {}", message),
        MessageType::Warning => format!("Warning: {}", message),
        MessageType::Info => message.to_string(),
    });
}
src/platform/withdrawals/mod.rs (1)

87-87: Document the rationale for the 100 document limit.

Consider adding a comment explaining why 100 was chosen as the limit and what happens if there are more withdrawals.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 8393b4f and fb365e0.

📒 Files selected for processing (3)
  • src/platform/mod.rs (5 hunks)
  • src/platform/withdrawals/mod.rs (1 hunks)
  • src/ui/withdraws_status_screen.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/platform/mod.rs
🧰 Additional context used
🔇 Additional comments (7)
src/ui/withdraws_status_screen.rs (4)

1-20: LGTM! Well-structured imports and thread-safe struct design.

The imports are comprehensive and the struct is properly designed with thread-safe constructs (Arc, Mutex) for concurrent access.


22-29: LGTM! Constructor properly initializes all fields.

The constructor correctly handles Arc cloning and initializes the fields with appropriate default values.


33-48: LGTM! Robust error and data handling.

The method properly handles both error messages and data display, with good mutex poisoning recovery.


125-132: LGTM! Well-implemented screen functionality.

The implementation properly handles state management, data updates, and UI layout with good error recovery.

Also applies to: 137-174

src/platform/withdrawals/mod.rs (3)

1-24: LGTM! Well-organized imports and clear constant definition.

The imports are properly scoped and the constant follows Rust naming conventions.


31-47: 🛠️ Refactor suggestion

Consider deriving Serialize and Deserialize traits.

Since these structures represent data that might need to be serialized (e.g., for API responses or storage), consider deriving the serde traits.

+use serde::{Serialize, Deserialize};

-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
 pub struct WithdrawRecord {
     // ...
 }

-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
 pub struct WithdrawStatusData {
     // ...
 }

Likely invalid or redundant comment.


65-89: Verify query performance and pagination.

The query is well-structured but consider:

  1. The impact of ordering by multiple fields on large datasets
  2. The absence of pagination might affect user experience if there are many withdrawals

Consider implementing pagination to handle large sets of withdrawals efficiently. This could be done by:

  1. Adding a start_after parameter to the query
  2. Returning a next_page_token in WithdrawStatusData

Comment thread src/ui/withdraws_status_screen.rs
Comment thread src/platform/withdrawals/mod.rs 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: 3

🧹 Outside diff range and nitpick comments (1)
src/platform/withdrawals/mod.rs (1)

87-87: Document the rationale for the 100 document limit.

The hardcoded limit of 100 documents should be documented to explain the reasoning behind this specific value. Consider making it configurable if appropriate.

Add a comment explaining the limit:

-            limit: 100,
+            // Limit set to 100 to balance between data freshness and performance
+            limit: 100,
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between fb365e0 and f254d64.

📒 Files selected for processing (1)
  • src/platform/withdrawals/mod.rs (1 hunks)

Comment on lines +61 to +64
pub(super) async fn query_withdrawals(
self: &Arc<Self>,
sdk: Sdk,
) -> Result<BackendTaskSuccessResult, String> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider implementing custom error types instead of using String.

Converting all errors to strings loses type information and makes error handling less robust. Consider implementing a custom error type using thiserror or similar.

Example implementation:

#[derive(Debug, thiserror::Error)]
pub enum WithdrawalError {
    #[error("SDK error: {0}")]
    SdkError(#[from] dash_sdk::Error),
    #[error("Missing sum tree value")]
    MissingSumTreeValue,
    #[error("Invalid withdrawal data: {0}")]
    InvalidData(String),
}

Then update the return type:

-    ) -> Result<BackendTaskSuccessResult, String> {
+    ) -> Result<BackendTaskSuccessResult, WithdrawalError> {

Comment thread src/platform/withdrawals/mod.rs Outdated
Comment thread src/platform/withdrawals/mod.rs 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: 4

🧹 Outside diff range and nitpick comments (1)
src/platform/withdrawals/mod.rs (1)

65-91: Consider implementing pagination support.

The query has a hardcoded limit of 100 records. For better scalability and user experience, consider:

  1. Making the limit configurable
  2. Implementing pagination using the start parameter
  3. Adding a mechanism to fetch the total count
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f254d64 and 5ab872d.

📒 Files selected for processing (2)
  • src/platform/withdrawals/mod.rs (1 hunks)
  • src/ui/withdraws_status_screen.rs (1 hunks)
🔇 Additional comments (5)
src/platform/withdrawals/mod.rs (3)

31-47: Consider deriving Serialize and Deserialize traits.

The data structures might need serialization capabilities for data transfer and storage.


61-64: Consider implementing custom error types.

Using String for errors loses type information. A custom error enum would provide better error handling.


202-205: Consider validating the address format.

While the code handles the error case, it might be helpful to validate the address format more specifically, possibly checking for:

  1. Network compatibility
  2. Address type (P2PKH, P2SH, etc.)
  3. Checksum validity
src/ui/withdraws_status_screen.rs (2)

1-32: LGTM! Well-structured type definitions.

The struct and enum definitions are well-organized with appropriate use of interior mutability for sort-related fields.


233-284: LGTM! Robust trait implementation.

The ScreenLike trait implementation is well-structured with proper error handling and clear separation of UI components.

Comment thread src/platform/withdrawals/mod.rs
Comment thread src/platform/withdrawals/mod.rs
Comment thread src/ui/withdraws_status_screen.rs
Comment thread src/ui/withdraws_status_screen.rs
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

🧹 Outside diff range and nitpick comments (4)
src/platform/withdrawals/mod.rs (3)

23-24: Enhance constant documentation.

The documentation comment could be more descriptive about the purpose and usage of this key in the withdrawal transactions sum tree.

-/// constant id for subtree containing the sum of withdrawals
+/// Key used to identify the subtree that maintains the running sum of all withdrawal
+/// transactions. This is used for efficient tracking of total withdrawal amounts
+/// without needing to scan all withdrawal documents.
 pub const WITHDRAWAL_TRANSACTIONS_SUM_AMOUNT_TREE_KEY: [u8; 1] = [2];

86-87: Consider making the document limit configurable.

The hard-coded limit of 100 documents might not be suitable for all use cases. Consider:

  1. Making this limit configurable
  2. Implementing proper pagination support using the start parameter

170-172: Improve error messages with more context.

The error messages could be more descriptive by including the field name and actual values where applicable.

-        .ok_or_else(|| "expected created at".to_string())?;
+        .ok_or_else(|| "Missing required field 'created_at' in withdrawal document".to_string())?;

-        .ok_or_else(|| "expected date time".to_string())?
+        .ok_or_else(|| format!("Invalid timestamp value: {}", index))?

-        .map_err(|_| "expected amount on withdrawal".to_string())?;
+        .map_err(|_| "Missing or invalid 'amount' field in withdrawal document".to_string())?;

-        .map_err(|_| "expected status on withdrawal".to_string())?;
+        .map_err(|_| "Missing or invalid 'status' field in withdrawal document".to_string())?;

-        .map_err(|_| "expected output script".to_string())?;
+        .map_err(|_| "Missing or invalid 'output_script' field in withdrawal document".to_string())?;

Also applies to: 174-176, 178-181, 183-186, 194-197

src/ui/withdraws_status_screen.rs (1)

337-388: Add error logging for mutex poisoning.

While the code handles mutex poisoning gracefully, adding error logging would help with debugging.

Apply this diff to add error logging:

     fn refresh(&mut self) {
         let mut lock_data = self.data.lock().unwrap_or_else(|poisoned| {
+            eprintln!("Mutex poisoned during refresh: {:?}", poisoned);
             // Mutex is poisoned, trying to recover the inner data
             poisoned.into_inner()
         });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 5ab872d and d55a09a.

📒 Files selected for processing (3)
  • src/platform/withdrawals/mod.rs (1 hunks)
  • src/ui/withdraws_status_screen.rs (1 hunks)
  • testnet_nodes.yml (1 hunks)
🧰 Additional context used
🪛 Gitleaks
testnet_nodes.yml

1-1: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (5)
src/ui/withdraws_status_screen.rs (5)

20-66: LGTM! Thread-safe state management implementation.

The struct is well-designed with appropriate use of Arc for thread-safe data handling and proper initialization of all filter states.


68-68: Empty method implementation.


70-86: LGTM! Robust error handling implementation.

The code properly handles mutex poisoning and provides appropriate error message display.


88-127: Repetitive sorting implementation.


314-334: LGTM! Clean filter status implementation.

The utility method efficiently manages filter states with clear separation of concerns.

Comment thread src/platform/withdrawals/mod.rs
Comment thread src/ui/withdraws_status_screen.rs
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: 5

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between d55a09a and cf75e01.

📒 Files selected for processing (1)
  • src/ui/withdraws_status_screen.rs (1 hunks)

Comment thread src/ui/withdraws_status_screen.rs
Comment thread src/ui/withdraws_status_screen.rs
Comment thread src/ui/withdraws_status_screen.rs
Comment thread src/ui/withdraws_status_screen.rs
Comment on lines +277 to +297
fn util_build_combined_filter_status_mix(&self) {
let mut res = vec![];
if self.filter_status_queued.get() {
res.push(Value::U8(WithdrawalStatus::QUEUED as u8));
}
if self.filter_status_pooled.get() {
res.push(Value::U8(WithdrawalStatus::POOLED as u8));
}
if self.filter_status_broadcasted.get() {
res.push(Value::U8(WithdrawalStatus::BROADCASTED as u8));
}
if self.filter_status_complete.get() {
res.push(Value::U8(WithdrawalStatus::COMPLETE as u8));
}
if self.filter_status_expired.get() {
res.push(Value::U8(WithdrawalStatus::EXPIRED as u8));
}

self.filter_status_mix.borrow_mut().clear();
self.filter_status_mix.borrow_mut().extend(res);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize filter status mix building.

The current implementation allocates a new vector and clears/extends the existing one.

Consider using a more efficient approach:

     fn util_build_combined_filter_status_mix(&self) {
-        let mut res = vec![];
+        let mut filter_mix = self.filter_status_mix.borrow_mut();
+        filter_mix.clear();
+        filter_mix.reserve(5); // Pre-allocate space for all possible statuses
         if self.filter_status_queued.get() {
-            res.push(Value::U8(WithdrawalStatus::QUEUED as u8));
+            filter_mix.push(Value::U8(WithdrawalStatus::QUEUED as u8));
         }
         // ... similar changes for other status checks ...
-        self.filter_status_mix.borrow_mut().clear();
-        self.filter_status_mix.borrow_mut().extend(res);
     }

Committable suggestion was skipped due to low confidence.

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

🧹 Outside diff range and nitpick comments (2)
src/ui/withdraws_status_screen.rs (2)

18-50: Add documentation for struct and enum declarations.

Consider adding documentation comments to improve code maintainability:

  • Document the purpose of WithdrawsStatusScreen and its fields
  • Add explanations for SortColumn and PaginationItemsPerPage enums
+/// Represents the withdrawal status screen UI component.
+/// Manages the display and interaction with withdrawal records including
+/// filtering, sorting, and pagination capabilities.
 pub struct WithdrawsStatusScreen {
+    /// Application context
     pub app_context: Arc<AppContext>,
+    /// Thread-safe storage for withdrawal status data
     data: Arc<Mutex<Option<WithdrawStatusData>>>,
     // ... add docs for other fields
 }

+/// Defines the available columns for sorting withdrawal records
 #[derive(Clone, Copy, PartialEq)]
 enum SortColumn {
     // ... add docs for variants
 }

218-218: Remove debug print statement.

Replace the println! with proper logging or remove it entirely.

-        println!("computing with:{}", self.pagination_items_per_page.get() as usize);
+        log::debug!("Computing pagination with {} items per page", self.pagination_items_per_page.get() as usize);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between cf75e01 and 2324a40.

📒 Files selected for processing (1)
  • src/ui/withdraws_status_screen.rs (1 hunks)
🔇 Additional comments (2)
src/ui/withdraws_status_screen.rs (2)

334-343: ⚠️ Potential issue

Consider potential race condition in display_task_result.

The method updates both the data and error_message without ensuring atomicity. A race condition could occur if another thread updates the error message between these operations.

     fn display_task_result(&mut self, backend_task_success_result: BackendTaskSuccessResult) {
         if let BackendTaskSuccessResult::WithdrawalStatus(data) = backend_task_success_result {
+            // Acquire mutex once for both operations
             let mut lock_data = self.data.lock().unwrap_or_else(|poisoned| {
-                // Mutex is poisoned, trying to recover the inner data
                 poisoned.into_inner()
             });
-            *lock_data = Some(data);
-            self.error_message = None;
+            // Update both fields atomically
+            *lock_data = Some(data);
+            self.error_message = None;
         }
     }

Likely invalid or redundant comment.


53-75: 🛠️ Refactor suggestion

Improve error handling in constructor.

The constructor should handle potential errors when creating the mutex.

-    pub fn new(app_context: &Arc<AppContext>) -> Self {
+    pub fn new(app_context: &Arc<AppContext>) -> Result<Self, std::sync::PoisonError<()>> {
+        let data = Arc::new(Mutex::new(None));
+        // Verify mutex is not poisoned
+        data.lock().map_err(|_| std::sync::PoisonError::new(()))?;
+        
-        Self {
+        Ok(Self {
-            data: Arc::new(Mutex::new(None)),
+            data,
             // ... rest of the initialization
-        }
+        })
     }

Likely invalid or redundant comment.

@QuantumExplorer QuantumExplorer changed the base branch from master to v0.2-dev October 25, 2024 13:18
@QuantumExplorer QuantumExplorer merged commit 1c720c2 into v0.2-dev Oct 25, 2024
@QuantumExplorer QuantumExplorer deleted the feat/withdraws_queue_screen branch October 25, 2024 13:20
This was referenced Nov 25, 2024
shumkov added a commit that referenced this pull request Apr 13, 2026
sent_requests and incoming_requests in the ContactChangeSet represent
pending contact requests (one side sent, waiting for reciprocation).
Writing them as 'accepted' was incorrect — they're not accepted until
both sides exchange requests and the contact is established.

Changed the INSERT status from 'accepted' to 'pending'. Established
contacts in the dashpay_contacts table already correctly write
'accepted' — that path is unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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