diff --git a/Cargo.lock b/Cargo.lock index 32a486d5e1..9c86101f47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3999,6 +3999,7 @@ dependencies = [ name = "tari_console_wallet" version = "0.8.11" dependencies = [ + "bitflags 1.2.1", "chrono", "chrono-english", "crossterm", diff --git a/applications/tari_console_wallet/Cargo.toml b/applications/tari_console_wallet/Cargo.toml index c5e2a2d715..0a2f15433d 100644 --- a/applications/tari_console_wallet/Cargo.toml +++ b/applications/tari_console_wallet/Cargo.toml @@ -16,6 +16,7 @@ tari_app_grpc = { path = "../tari_app_grpc" } tari_shutdown = { path = "../../infrastructure/shutdown" } tari_key_manager = { path = "../../base_layer/key_manager" } +bitflags = "1.2.1" chrono = { version = "0.4.6", features = ["serde"]} chrono-english = "0.1" futures = { version = "^0.3.1", default-features = false, features = ["alloc"]} diff --git a/applications/tari_console_wallet/src/ui/components/transactions_tab.rs b/applications/tari_console_wallet/src/ui/components/transactions_tab.rs index f67efc0f92..7789e38679 100644 --- a/applications/tari_console_wallet/src/ui/components/transactions_tab.rs +++ b/applications/tari_console_wallet/src/ui/components/transactions_tab.rs @@ -145,13 +145,13 @@ impl TransactionsTab { .title(Span::styled("Completed (T)ransactions", style)); f.render_widget(block, list_areas[1]); - self.completed_list_state - .set_num_items(app_state.get_completed_txs().len()); + let completed_txs = app_state.get_completed_txs(); + self.completed_list_state.set_num_items(completed_txs.len()); let mut completed_list_state = self .completed_list_state .get_list_state((list_areas[1].height as usize).saturating_sub(3)); let window = self.completed_list_state.get_start_end(); - let windowed_view = app_state.get_completed_txs_slice(window.0, window.1); + let windowed_view = &completed_txs[window.0..window.1]; let mut column0_items = Vec::new(); let mut column1_items = Vec::new(); @@ -188,7 +188,9 @@ impl TransactionsTab { format!("{}", local_time.format("%Y-%m-%d %H:%M:%S")), Style::default().fg(text_color), ))); - let status = if t.cancelled { + let status = if t.cancelled && t.status == TransactionStatus::Coinbase { + "Abandoned".to_string() + } else if t.cancelled { "Cancelled".to_string() } else if !t.valid { "Invalid".to_string() @@ -425,6 +427,8 @@ impl Component for TransactionsTab { span_vec.push(Span::raw(" selects a transaction, ")); span_vec.push(Span::styled("C", Style::default().add_modifier(Modifier::BOLD))); span_vec.push(Span::raw(" cancels a selected Pending Tx, ")); + span_vec.push(Span::styled("A", Style::default().add_modifier(Modifier::BOLD))); + span_vec.push(Span::raw(" shows abandoned coinbase Txs, ")); span_vec.push(Span::styled("Esc", Style::default().add_modifier(Modifier::BOLD))); span_vec.push(Span::raw(" exits the list.")); @@ -512,6 +516,7 @@ impl Component for TransactionsTab { self.confirmation_dialog = true; } }, + 'a' => app_state.toggle_abandoned_coinbase_filter(), '\n' => match self.selected_tx_list { SelectedTransactionList::None => {}, SelectedTransactionList::PendingTxs => { diff --git a/applications/tari_console_wallet/src/ui/state/app_state.rs b/applications/tari_console_wallet/src/ui/state/app_state.rs index d2fa501b6e..8812d3bf4d 100644 --- a/applications/tari_console_wallet/src/ui/state/app_state.rs +++ b/applications/tari_console_wallet/src/ui/state/app_state.rs @@ -33,6 +33,7 @@ use crate::{ utils::db::{CUSTOM_BASE_NODE_ADDRESS_KEY, CUSTOM_BASE_NODE_PUBLIC_KEY_KEY}, wallet_modes::PeerConfig, }; +use bitflags::bitflags; use futures::{stream::Fuse, StreamExt}; use log::*; use qrcode::{render::unicode, QrCode}; @@ -71,6 +72,7 @@ const LOG_TARGET: &str = "wallet::console_wallet::app_state"; pub struct AppState { inner: Arc>, cached_data: AppStateData, + completed_tx_filter: TransactionFilter, node_config: GlobalConfig, } @@ -85,9 +87,11 @@ impl AppState { ) -> Self { let inner = AppStateInner::new(node_identity, network, wallet, base_node_selected, base_node_config); let cached_data = inner.data.clone(); + Self { inner: Arc::new(RwLock::new(inner)), cached_data, + completed_tx_filter: TransactionFilter::ABANDONED_COINBASES, node_config, } } @@ -100,33 +104,31 @@ impl AppState { pub async fn refresh_transaction_state(&mut self) -> Result<(), UiError> { let mut inner = self.inner.write().await; inner.refresh_full_transaction_state().await?; - if let Some(data) = inner.get_updated_app_state() { - self.cached_data = data; - } + drop(inner); + self.update_cache().await; Ok(()) } pub async fn refresh_contacts_state(&mut self) -> Result<(), UiError> { let mut inner = self.inner.write().await; inner.refresh_contacts_state().await?; - if let Some(data) = inner.get_updated_app_state() { - self.cached_data = data; - } + drop(inner); + self.update_cache().await; Ok(()) } pub async fn refresh_connected_peers_state(&mut self) -> Result<(), UiError> { let mut inner = self.inner.write().await; inner.refresh_connected_peers_state().await?; - if let Some(data) = inner.get_updated_app_state() { - self.cached_data = data; - } + drop(inner); + self.update_cache().await; Ok(()) } pub async fn update_cache(&mut self) { let mut inner = self.inner.write().await; - if let Some(data) = inner.get_updated_app_state() { + let updated_state = inner.get_updated_app_state(); + if let Some(data) = updated_state { self.cached_data = data; } } @@ -145,9 +147,8 @@ impl AppState { inner.wallet.contacts_service.upsert_contact(contact).await?; inner.refresh_contacts_state().await?; - if let Some(data) = inner.get_updated_app_state() { - self.cached_data = data; - } + drop(inner); + self.update_cache().await; Ok(()) } @@ -161,9 +162,8 @@ impl AppState { inner.wallet.contacts_service.remove_contact(public_key).await?; inner.refresh_contacts_state().await?; - if let Some(data) = inner.get_updated_app_state() { - self.cached_data = data; - } + drop(inner); + self.update_cache().await; Ok(()) } @@ -274,16 +274,19 @@ impl AppState { } } - pub fn get_completed_txs_slice(&self, start: usize, end: usize) -> &[CompletedTransaction] { - if self.cached_data.completed_txs.is_empty() || start > end || end > self.cached_data.completed_txs.len() { - return &[]; + pub fn get_completed_txs(&self) -> Vec<&CompletedTransaction> { + if self + .completed_tx_filter + .contains(TransactionFilter::ABANDONED_COINBASES) + { + self.cached_data + .completed_txs + .iter() + .filter(|tx| !(tx.cancelled && tx.status == TransactionStatus::Coinbase)) + .collect() + } else { + self.cached_data.completed_txs.iter().collect() } - - &self.cached_data.completed_txs[start..end] - } - - pub fn get_completed_txs(&self) -> &Vec { - &self.cached_data.completed_txs } pub fn get_confirmations(&self, tx_id: &TxId) -> Option<&u64> { @@ -291,8 +294,9 @@ impl AppState { } pub fn get_completed_tx(&self, index: usize) -> Option<&CompletedTransaction> { - if index < self.cached_data.completed_txs.len() { - Some(&self.cached_data.completed_txs[index]) + let filtered_completed_txs = self.get_completed_txs(); + if index < filtered_completed_txs.len() { + Some(filtered_completed_txs[index]) } else { None } @@ -363,6 +367,10 @@ impl AppState { pub fn get_required_confirmations(&self) -> u64 { (&self.node_config.transaction_num_confirmations_required).to_owned() } + + pub fn toggle_abandoned_coinbase_filter(&mut self) { + self.completed_tx_filter.toggle(TransactionFilter::ABANDONED_COINBASES); + } } pub struct AppStateInner { @@ -864,3 +872,10 @@ pub enum UiTransactionSendStatus { SentViaSaf, Error(String), } + +bitflags! { + pub struct TransactionFilter: u8 { + const NONE = 0b0000_0000; + const ABANDONED_COINBASES = 0b0000_0001; + } +}