From 22271c13f3c69b5047e399113c8fb8e06d21fd0e Mon Sep 17 00:00:00 2001 From: Paul DeLucia <69597248+pauldelucia@users.noreply.github.com> Date: Sun, 17 Nov 2024 11:02:43 +0700 Subject: [PATCH] fix: error messages in withdrawal screen and wallet unlocking (#54) * fix: withdrawal screen messages * wallet unlock for withdrawal screen * fix * restructure identity files * renames withdrawals * fix --- src/app.rs | 2 +- src/backend_task/mod.rs | 4 +- .../mod.rs | 0 .../identities/add_new_identity_screen/mod.rs | 14 -- src/ui/identities/identities_screen.rs | 7 +- .../{ => identities/keys}/add_key_screen.rs | 0 .../{ => identities/keys}/key_info_screen.rs | 0 src/ui/{ => identities/keys}/keys_screen.rs | 0 src/ui/identities/keys/mod.rs | 3 + src/ui/identities/mod.rs | 2 + .../identities/register_dpns_name_screen.rs | 8 +- .../withdraw_from_identity_screen.rs} | 190 ++++++++++++++++-- src/ui/mod.rs | 16 +- src/ui/transfers/mod.rs | 2 +- ...creen.rs => withdrawal_statuses_screen.rs} | 4 +- 15 files changed, 199 insertions(+), 53 deletions(-) rename src/backend_task/{withdrawals => withdrawal_statuses}/mod.rs (100%) rename src/ui/{ => identities/keys}/add_key_screen.rs (100%) rename src/ui/{ => identities/keys}/key_info_screen.rs (100%) rename src/ui/{ => identities/keys}/keys_screen.rs (100%) create mode 100644 src/ui/identities/keys/mod.rs rename src/ui/{withdrawals/mod.rs => identities/withdraw_from_identity_screen.rs} (58%) rename src/ui/{withdraws_status_screen.rs => withdrawal_statuses_screen.rs} (99%) diff --git a/src/app.rs b/src/app.rs index 87bf2703..009bc2f9 100644 --- a/src/app.rs +++ b/src/app.rs @@ -15,7 +15,7 @@ use crate::ui::network_chooser_screen::NetworkChooserScreen; use crate::ui::tool_screens::proof_log_screen::ProofLogScreen; use crate::ui::tool_screens::transition_visualizer_screen::TransitionVisualizerScreen; use crate::ui::wallet::wallets_screen::WalletsBalancesScreen; -use crate::ui::withdraws_status_screen::WithdrawsStatusScreen; +use crate::ui::withdrawal_statuses_screen::WithdrawsStatusScreen; use crate::ui::{MessageType, RootScreenType, Screen, ScreenLike, ScreenType}; use dash_sdk::dpp::dashcore::Network; use derive_more::From; diff --git a/src/backend_task/mod.rs b/src/backend_task/mod.rs index ff3c41fa..e4e4fca2 100644 --- a/src/backend_task/mod.rs +++ b/src/backend_task/mod.rs @@ -4,7 +4,7 @@ use crate::backend_task::contract::ContractTask; use crate::backend_task::core::{CoreItem, CoreTask}; use crate::backend_task::document::DocumentTask; use crate::backend_task::identity::IdentityTask; -use crate::backend_task::withdrawals::{WithdrawStatusPartialData, WithdrawalsTask}; +use crate::backend_task::withdrawal_statuses::{WithdrawStatusPartialData, WithdrawalsTask}; use crate::context::AppContext; use crate::model::qualified_identity::QualifiedIdentity; use dash_sdk::dpp::voting::votes::Vote; @@ -18,7 +18,7 @@ pub mod contract; pub mod core; mod document; pub mod identity; -pub mod withdrawals; +pub mod withdrawal_statuses; #[derive(Debug, Clone, PartialEq)] pub(crate) enum BackendTask { diff --git a/src/backend_task/withdrawals/mod.rs b/src/backend_task/withdrawal_statuses/mod.rs similarity index 100% rename from src/backend_task/withdrawals/mod.rs rename to src/backend_task/withdrawal_statuses/mod.rs diff --git a/src/ui/identities/add_new_identity_screen/mod.rs b/src/ui/identities/add_new_identity_screen/mod.rs index 997366e5..3eff547c 100644 --- a/src/ui/identities/add_new_identity_screen/mod.rs +++ b/src/ui/identities/add_new_identity_screen/mod.rs @@ -10,16 +10,13 @@ use crate::backend_task::identity::{ }; use crate::backend_task::{BackendTask, BackendTaskSuccessResult}; use crate::context::AppContext; -use crate::model::qualified_identity::QualifiedIdentity; use crate::model::wallet::Wallet; use crate::ui::components::top_panel::add_top_panel; use crate::ui::components::wallet_unlock::ScreenWithWalletUnlock; use crate::ui::{MessageType, ScreenLike}; use arboard::Clipboard; -use dash_sdk::dashcore_rpc::dashcore::address::Error; use dash_sdk::dashcore_rpc::dashcore::transaction::special_transaction::TransactionPayload; use dash_sdk::dashcore_rpc::dashcore::Address; -use dash_sdk::dashcore_rpc::RpcApi; use dash_sdk::dpp::balances::credits::Duffs; use dash_sdk::dpp::dashcore::{OutPoint, PrivateKey, Transaction, TxOut}; use dash_sdk::dpp::identity::accessors::IdentityGettersV0; @@ -30,22 +27,11 @@ use eframe::egui::Context; use egui::ahash::HashSet; use egui::{Color32, ColorImage, ComboBox, ScrollArea, Ui}; use image::Luma; -use itertools::Itertools; use qrcode::QrCode; -use serde::Deserialize; use std::cmp::PartialEq; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::{fmt, thread}; -use zeroize::Zeroize; - -#[derive(Debug, Clone, Deserialize)] -struct KeyInfo { - address: String, - #[serde(rename = "private_key")] - private_key: String, -} #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum FundingMethod { diff --git a/src/ui/identities/identities_screen.rs b/src/ui/identities/identities_screen.rs index 4103ca56..c19d4a5c 100644 --- a/src/ui/identities/identities_screen.rs +++ b/src/ui/identities/identities_screen.rs @@ -10,12 +10,11 @@ use crate::model::qualified_identity::PrivateKeyTarget::{ }; use crate::model::qualified_identity::{IdentityType, QualifiedIdentity}; use crate::model::wallet::WalletSeedHash; -use crate::ui::add_key_screen::AddKeyScreen; use crate::ui::components::left_panel::add_left_panel; use crate::ui::components::top_panel::add_top_panel; -use crate::ui::key_info_screen::KeyInfoScreen; +use crate::ui::identities::keys::add_key_screen::AddKeyScreen; +use crate::ui::identities::keys::key_info_screen::KeyInfoScreen; use crate::ui::transfers::TransferScreen; -use crate::ui::withdrawals::WithdrawalScreen; use crate::ui::{RootScreenType, Screen, ScreenLike, ScreenType}; use dash_sdk::dpp::identity::accessors::IdentityGettersV0; use dash_sdk::dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0; @@ -32,6 +31,8 @@ use std::collections::HashMap; use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; +use super::withdraw_from_identity_screen::WithdrawalScreen; + pub struct IdentitiesScreen { pub identities: Arc>>, pub app_context: Arc, diff --git a/src/ui/add_key_screen.rs b/src/ui/identities/keys/add_key_screen.rs similarity index 100% rename from src/ui/add_key_screen.rs rename to src/ui/identities/keys/add_key_screen.rs diff --git a/src/ui/key_info_screen.rs b/src/ui/identities/keys/key_info_screen.rs similarity index 100% rename from src/ui/key_info_screen.rs rename to src/ui/identities/keys/key_info_screen.rs diff --git a/src/ui/keys_screen.rs b/src/ui/identities/keys/keys_screen.rs similarity index 100% rename from src/ui/keys_screen.rs rename to src/ui/identities/keys/keys_screen.rs diff --git a/src/ui/identities/keys/mod.rs b/src/ui/identities/keys/mod.rs new file mode 100644 index 00000000..688c6dbf --- /dev/null +++ b/src/ui/identities/keys/mod.rs @@ -0,0 +1,3 @@ +pub mod add_key_screen; +pub mod key_info_screen; +pub mod keys_screen; diff --git a/src/ui/identities/mod.rs b/src/ui/identities/mod.rs index a4f08b09..4a35447c 100644 --- a/src/ui/identities/mod.rs +++ b/src/ui/identities/mod.rs @@ -1,4 +1,6 @@ pub mod add_existing_identity_screen; pub mod add_new_identity_screen; pub mod identities_screen; +pub mod keys; pub mod register_dpns_name_screen; +pub mod withdraw_from_identity_screen; diff --git a/src/ui/identities/register_dpns_name_screen.rs b/src/ui/identities/register_dpns_name_screen.rs index dc5cd5c7..bb207aa9 100644 --- a/src/ui/identities/register_dpns_name_screen.rs +++ b/src/ui/identities/register_dpns_name_screen.rs @@ -256,15 +256,11 @@ impl ScreenLike for RegisterDpnsNameScreen { return; } - let mut step = 1; - // Select the identity to register the name for ui.heading("1. Select Identity"); ui.add_space(5.0); self.render_identity_id_selection(ui); - step += 1; - ui.add_space(10.0); ui.separator(); ui.add_space(10.0); @@ -278,7 +274,7 @@ impl ScreenLike for RegisterDpnsNameScreen { } // Input for the name - ui.heading(format!("{}. Enter the Name to Register:", step)); + ui.heading("2. Enter the Name to Register:"); ui.add_space(5.0); ui.horizontal(|ui| { ui.label("Name (without \".dash\"):"); @@ -296,7 +292,7 @@ impl ScreenLike for RegisterDpnsNameScreen { ); ui.colored_label( egui::Color32::DARK_RED, - "Cost ≈ 0.2 Dash", + "Cost ≈ 0.2006 Dash", ); } else { ui.colored_label( diff --git a/src/ui/withdrawals/mod.rs b/src/ui/identities/withdraw_from_identity_screen.rs similarity index 58% rename from src/ui/withdrawals/mod.rs rename to src/ui/identities/withdraw_from_identity_screen.rs index a7840ead..3126a758 100644 --- a/src/ui/withdrawals/mod.rs +++ b/src/ui/identities/withdraw_from_identity_screen.rs @@ -2,29 +2,47 @@ use crate::app::AppAction; use crate::backend_task::identity::IdentityTask; use crate::backend_task::BackendTask; use crate::context::AppContext; -use crate::model::qualified_identity::{IdentityType, QualifiedIdentity}; +use crate::model::qualified_identity::encrypted_key_storage::PrivateKeyData; +use crate::model::qualified_identity::{IdentityType, PrivateKeyTarget, QualifiedIdentity}; +use crate::model::wallet::Wallet; use crate::ui::components::top_panel::add_top_panel; -use crate::ui::key_info_screen::KeyInfoScreen; +use crate::ui::components::wallet_unlock::ScreenWithWalletUnlock; use crate::ui::{MessageType, Screen, ScreenLike}; use dash_sdk::dashcore_rpc::dashcore::Address; use dash_sdk::dpp::fee::Credits; use dash_sdk::dpp::identity::accessors::IdentityGettersV0; use dash_sdk::dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0; use dash_sdk::dpp::identity::{KeyType, Purpose, SecurityLevel}; +use dash_sdk::dpp::prelude::TimestampMillis; use dash_sdk::platform::IdentityPublicKey; use eframe::egui::{self, Context, Ui}; +use egui::{Color32, RichText}; use std::str::FromStr; -use std::sync::Arc; +use std::sync::{Arc, RwLock}; +use std::time::{SystemTime, UNIX_EPOCH}; + +use super::keys::key_info_screen::KeyInfoScreen; + +pub enum WithdrawFromIdentityStatus { + NotStarted, + WaitingForResult(TimestampMillis), + ErrorMessage(String), + Complete, +} pub struct WithdrawalScreen { pub identity: QualifiedIdentity, selected_key: Option, withdrawal_address: String, withdrawal_amount: String, - error_message: Option, max_amount: u64, pub app_context: Arc, confirmation_popup: bool, + withdraw_from_identity_status: WithdrawFromIdentityStatus, + selected_wallet: Option>>, + wallet_password: String, + show_password: bool, + error_message: Option, } impl WithdrawalScreen { @@ -35,10 +53,14 @@ impl WithdrawalScreen { selected_key: None, withdrawal_address: String::new(), withdrawal_amount: String::new(), - error_message: None, max_amount, app_context: app_context.clone(), confirmation_popup: false, + withdraw_from_identity_status: WithdrawFromIdentityStatus::NotStarted, + selected_wallet: None, + wallet_password: String::new(), + show_password: false, + error_message: None, } } @@ -78,7 +100,7 @@ impl WithdrawalScreen { fn render_amount_input(&mut self, ui: &mut Ui) { ui.horizontal(|ui| { - ui.label("Amount:"); + ui.label("Amount (dash):"); ui.text_edit_singleline(&mut self.withdrawal_amount); @@ -122,7 +144,10 @@ impl WithdrawalScreen { match Address::from_str(&self.withdrawal_address) { Ok(address) => Some(address.assume_checked()), Err(_) => { - self.error_message = Some("Invalid withdrawal address".to_string()); + self.withdraw_from_identity_status = + WithdrawFromIdentityStatus::ErrorMessage( + "Invalid withdrawal address".to_string(), + ); None } } @@ -136,14 +161,17 @@ impl WithdrawalScreen { { format!("masternode payout address {}", payout_address) } else if !self.app_context.developer_mode { - self.error_message = Some("No masternode payout address".to_string()); + self.withdraw_from_identity_status = WithdrawFromIdentityStatus::ErrorMessage( + "No masternode payout address".to_string(), + ); return; } else { "to default address".to_string() }; let Some(selected_key) = self.selected_key.as_ref() else { - self.error_message = Some("No selected key".to_string()); + self.withdraw_from_identity_status = + WithdrawFromIdentityStatus::ErrorMessage("No selected key".to_string()); return; }; @@ -172,6 +200,12 @@ impl WithdrawalScreen { if ui.button("Confirm").clicked() { self.confirmation_popup = false; + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("Time went backwards") + .as_secs(); + self.withdraw_from_identity_status = + WithdrawFromIdentityStatus::WaitingForResult(now); app_action = AppAction::BackendTask(BackendTask::IdentityTask( IdentityTask::WithdrawFromIdentity( self.identity.clone(), @@ -193,8 +227,20 @@ impl WithdrawalScreen { } impl ScreenLike for WithdrawalScreen { - fn display_message(&mut self, message: &str, _message_type: MessageType) { - self.error_message = Some(message.to_string()); + fn display_message(&mut self, message: &str, message_type: MessageType) { + match message_type { + MessageType::Success => { + if message == "Successfully withdrew from identity" { + self.withdraw_from_identity_status = WithdrawFromIdentityStatus::Complete; + } + } + MessageType::Info => {} + MessageType::Error => { + // It's not great because the error message can be coming from somewhere else if there are other processes happening + self.withdraw_from_identity_status = + WithdrawFromIdentityStatus::ErrorMessage(message.to_string()); + } + } } /// Renders the UI components for the withdrawal screen @@ -217,7 +263,9 @@ impl ScreenLike for WithdrawalScreen { }; if !has_keys { - ui.heading(format!("You do not have any withdrawal keys loaded for this {}.", self.identity.identity_type)); + ui.heading(format!("You do not have any withdrawal keys loaded for this {} identity.", self.identity.identity_type)); + + ui.add_space(10.0); if self.identity.identity_type != IdentityType::User { ui.heading("An evonode can withdraw with the payout address private key or the owner key.".to_string()); @@ -240,7 +288,12 @@ impl ScreenLike for WithdrawalScreen { } if let Some(transfer_key) = transfer_key { - if ui.button("Check Payout Address Key").clicked() { + let key_type_name = match self.identity.identity_type { + IdentityType::User => "Transfer", + IdentityType::Masternode => "Payout", + IdentityType::Evonode => "Payout", + }; + if ui.button(format!("Check {} Address Key", key_type_name)).clicked() { action |= AppAction::AddScreen(Screen::KeyInfoScreen(KeyInfoScreen::new( self.identity.clone(), transfer_key.clone(), @@ -252,11 +305,43 @@ impl ScreenLike for WithdrawalScreen { } else { ui.heading("Withdraw Funds"); + ui.add_space(10.0); + self.render_key_selection(ui); + + ui.add_space(10.0); + + if let Some(selected_key) = self.selected_key.as_ref() { + // If there is an associated wallet then render the wallet unlock component for it if its locked + if let Some((_, PrivateKeyData::AtWalletDerivationPath(wallet_derivation_path))) = self.identity.private_keys.private_keys.get(&(PrivateKeyTarget::PrivateKeyOnMainIdentity, selected_key.id())) { + self.selected_wallet = self.identity.associated_wallets.get(&wallet_derivation_path.wallet_seed_hash).cloned(); + + let (needed_unlock, just_unlocked) = self.render_wallet_unlock_if_needed(ui); + + if needed_unlock && !just_unlocked { + return; + } + } + } else { + return; + } + self.render_amount_input(ui); + + ui.add_space(10.0); + self.render_address_input(ui); - if ui.button("Withdraw").clicked() { + ui.add_space(20.0); + + // Withdraw button + let button = egui::Button::new(RichText::new("Withdraw").color(Color32::WHITE)) + .fill(Color32::from_rgb(0, 128, 255)) + .frame(true) + .rounding(3.0) + .min_size(egui::vec2(60.0, 30.0)); + + if ui.add(button).clicked() { self.confirmation_popup = true; } @@ -264,7 +349,52 @@ impl ScreenLike for WithdrawalScreen { action |= self.show_confirmation_popup(ui); } - if let Some(error_message) = &self.error_message { + ui.add_space(10.0); + + // Handle withdrawal status messages + match &self.withdraw_from_identity_status { + WithdrawFromIdentityStatus::NotStarted => { + // Do nothing + } + WithdrawFromIdentityStatus::WaitingForResult(start_time) => { + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("Time went backwards") + .as_secs(); + let elapsed_seconds = now - start_time; + + let display_time = if elapsed_seconds < 60 { + format!( + "{} second{}", + elapsed_seconds, + if elapsed_seconds == 1 { "" } else { "s" } + ) + } else { + let minutes = elapsed_seconds / 60; + let seconds = elapsed_seconds % 60; + format!( + "{} minute{} and {} second{}", + minutes, + if minutes == 1 { "" } else { "s" }, + seconds, + if seconds == 1 { "" } else { "s" } + ) + }; + + ui.label(format!( + "Withdrawing... Time taken so far: {}", + display_time + )); + } + WithdrawFromIdentityStatus::ErrorMessage(msg) => { + ui.colored_label(egui::Color32::RED, format!("Error: {}", msg)); + } + WithdrawFromIdentityStatus::Complete => { + ui.colored_label(egui::Color32::DARK_GREEN, format!("Successfully withdrew from identity")); + } + } + + if let WithdrawFromIdentityStatus::ErrorMessage(ref error_message) = self.withdraw_from_identity_status { ui.label(format!("Error: {}", error_message)); } } @@ -272,3 +402,33 @@ impl ScreenLike for WithdrawalScreen { action } } + +impl ScreenWithWalletUnlock for WithdrawalScreen { + fn selected_wallet_ref(&self) -> &Option>> { + &self.selected_wallet + } + + fn wallet_password_ref(&self) -> &String { + &self.wallet_password + } + + fn wallet_password_mut(&mut self) -> &mut String { + &mut self.wallet_password + } + + fn show_password(&self) -> bool { + self.show_password + } + + fn show_password_mut(&mut self) -> &mut bool { + &mut self.show_password + } + + fn set_error_message(&mut self, error_message: Option) { + self.error_message = error_message; + } + + fn error_message(&self) -> Option<&String> { + self.error_message.as_ref() + } +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 69af5a7a..556879dd 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -5,18 +5,18 @@ use crate::model::qualified_identity::encrypted_key_storage::{ PrivateKeyData, WalletDerivationPath, }; use crate::model::qualified_identity::QualifiedIdentity; -use crate::ui::add_key_screen::AddKeyScreen; use crate::ui::document_query_screen::DocumentQueryScreen; use crate::ui::dpns_contested_names_screen::DPNSContestedNamesScreen; -use crate::ui::key_info_screen::KeyInfoScreen; -use crate::ui::keys_screen::KeysScreen; +use crate::ui::identities::keys::add_key_screen::AddKeyScreen; +use crate::ui::identities::keys::key_info_screen::KeyInfoScreen; +use crate::ui::identities::keys::keys_screen::KeysScreen; +use crate::ui::identities::withdraw_from_identity_screen::WithdrawalScreen; use crate::ui::network_chooser_screen::NetworkChooserScreen; use crate::ui::tool_screens::proof_log_screen::ProofLogScreen; use crate::ui::transfers::TransferScreen; use crate::ui::wallet::import_wallet_screen::ImportWalletScreen; use crate::ui::wallet::wallets_screen::WalletsBalancesScreen; -use crate::ui::withdrawals::WithdrawalScreen; -use crate::ui::withdraws_status_screen::WithdrawsStatusScreen; +use crate::ui::withdrawal_statuses_screen::WithdrawsStatusScreen; use dash_sdk::dpp::identity::Identity; use dash_sdk::dpp::prelude::IdentityPublicKey; use dpns_contested_names_screen::DPNSSubscreen; @@ -31,19 +31,15 @@ use std::sync::Arc; use tool_screens::transition_visualizer_screen::TransitionVisualizerScreen; use wallet::add_new_wallet_screen::AddNewWalletScreen; -mod add_key_screen; pub mod components; pub mod document_query_screen; pub mod dpns_contested_names_screen; pub(crate) mod identities; -pub mod key_info_screen; -pub mod keys_screen; pub mod network_chooser_screen; pub mod tool_screens; pub mod transfers; pub(crate) mod wallet; -pub mod withdrawals; -pub mod withdraws_status_screen; +pub mod withdrawal_statuses_screen; #[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash)] pub enum RootScreenType { diff --git a/src/ui/transfers/mod.rs b/src/ui/transfers/mod.rs index 9e49992f..1a0379e7 100644 --- a/src/ui/transfers/mod.rs +++ b/src/ui/transfers/mod.rs @@ -4,7 +4,7 @@ use crate::backend_task::BackendTask; use crate::context::AppContext; use crate::model::qualified_identity::QualifiedIdentity; use crate::ui::components::top_panel::add_top_panel; -use crate::ui::key_info_screen::KeyInfoScreen; +use crate::ui::identities::keys::key_info_screen::KeyInfoScreen; use crate::ui::{MessageType, Screen, ScreenLike}; use dash_sdk::dpp::fee::Credits; use dash_sdk::dpp::identity::accessors::IdentityGettersV0; diff --git a/src/ui/withdraws_status_screen.rs b/src/ui/withdrawal_statuses_screen.rs similarity index 99% rename from src/ui/withdraws_status_screen.rs rename to src/ui/withdrawal_statuses_screen.rs index 5ad1b12a..7063fa2d 100644 --- a/src/ui/withdraws_status_screen.rs +++ b/src/ui/withdrawal_statuses_screen.rs @@ -1,5 +1,7 @@ use crate::app::{AppAction, DesiredAppAction}; -use crate::backend_task::withdrawals::{WithdrawRecord, WithdrawStatusData, WithdrawalsTask}; +use crate::backend_task::withdrawal_statuses::{ + WithdrawRecord, WithdrawStatusData, WithdrawalsTask, +}; use crate::backend_task::{BackendTask, BackendTaskSuccessResult}; use crate::context::AppContext; use crate::ui::components::left_panel::add_left_panel;