diff --git a/src/backend_task/identity/mod.rs b/src/backend_task/identity/mod.rs index 8b6f6c37..ba81ba89 100644 --- a/src/backend_task/identity/mod.rs +++ b/src/backend_task/identity/mod.rs @@ -2,6 +2,7 @@ mod add_key_to_identity; mod load_identity; mod load_identity_from_wallet; mod refresh_identity; +mod refresh_loaded_identities_dpns_names; mod register_dpns_name; mod register_identity; mod top_up_identity; @@ -255,6 +256,7 @@ pub(crate) enum IdentityTask { Transfer(QualifiedIdentity, Identifier, Credits, Option), RegisterDpnsName(RegisterDpnsNameInput), RefreshIdentity(QualifiedIdentity), + RefreshLoadedIdentitiesOwnedDPNSNames, } fn verify_key_input( @@ -467,6 +469,9 @@ impl AppContext { IdentityTask::TopUpIdentity(top_up_info) => { self.top_up_identity(top_up_info, sender).await } + IdentityTask::RefreshLoadedIdentitiesOwnedDPNSNames => { + self.refresh_loaded_identities_dpns_names(sender).await + } } } } diff --git a/src/backend_task/identity/refresh_loaded_identities_dpns_names.rs b/src/backend_task/identity/refresh_loaded_identities_dpns_names.rs new file mode 100644 index 00000000..fc7da3c3 --- /dev/null +++ b/src/backend_task/identity/refresh_loaded_identities_dpns_names.rs @@ -0,0 +1,85 @@ +use super::BackendTaskSuccessResult; +use crate::app::TaskResult; +use crate::context::AppContext; +use crate::model::qualified_identity::DPNSNameInfo; +use dash_sdk::dpp::document::DocumentV0Getters; +use dash_sdk::dpp::identity::accessors::IdentityGettersV0; +use dash_sdk::dpp::platform_value::Value; +use dash_sdk::drive::query::{WhereClause, WhereOperator}; +use dash_sdk::platform::{Document, DocumentQuery, FetchMany}; +use tokio::sync::mpsc; + +impl AppContext { + pub(super) async fn refresh_loaded_identities_dpns_names( + &self, + sender: mpsc::Sender, + ) -> Result { + let qualified_identities = self + .load_local_qualified_identities() + .map_err(|e| format!("Error refreshing owned DPNS names: Database error: {}", e))?; + + for mut qualified_identity in qualified_identities { + let identity_id = qualified_identity.identity.id(); + + // Fetch DPNS names using SDK + let dpns_names_document_query = DocumentQuery { + data_contract: self.dpns_contract.clone(), + document_type_name: "domain".to_string(), + where_clauses: vec![WhereClause { + field: "records.identity".to_string(), + operator: WhereOperator::Equal, + value: Value::Identifier(identity_id.into()), + }], + order_by_clauses: vec![], + limit: 100, + start: None, + }; + + let owned_dpns_names = Document::fetch_many(&self.sdk, dpns_names_document_query) + .await + .map(|document_map| { + document_map + .values() + .filter_map(|maybe_doc| { + maybe_doc.as_ref().and_then(|doc| { + let name = doc + .get("normalizedLabel") + .map(|label| label.to_str().unwrap_or_default()); + let acquired_at = doc + .created_at() + .into_iter() + .chain(doc.transferred_at()) + .max(); + + match (name, acquired_at) { + (Some(name), Some(acquired_at)) => Some(DPNSNameInfo { + name: name.to_string(), + acquired_at, + }), + _ => None, + } + }) + }) + .collect::>() + }) + .map_err(|e| format!("Error refreshing owned DPNS names: {}", e))?; + + qualified_identity.dpns_names = owned_dpns_names; + + // Update qualified identity in the database + self.update_local_qualified_identity(&qualified_identity) + .map_err(|e| format!("Error refreshing owned DPNS names: Database error: {}", e))?; + } + + sender.send(TaskResult::Refresh).await.map_err(|e| { + format!( + "Error refreshing owned DPNS names. Sender failed to send TaskResult: {}", + e.to_string() + ) + })?; + + Ok(BackendTaskSuccessResult::Message( + "Successfully refreshed loaded identities dpns names".to_string(), + )) + } +} diff --git a/src/ui/dpns_contested_names_screen.rs b/src/ui/dpns_contested_names_screen.rs index 3221a804..cca37e98 100644 --- a/src/ui/dpns_contested_names_screen.rs +++ b/src/ui/dpns_contested_names_screen.rs @@ -1,8 +1,8 @@ use super::components::dpns_subscreen_chooser_panel::add_dpns_subscreen_chooser_panel; -use super::components::top_panel; use super::{Screen, ScreenType}; use crate::app::{AppAction, DesiredAppAction}; use crate::backend_task::contested_names::ContestedResourceTask; +use crate::backend_task::identity::IdentityTask; use crate::backend_task::BackendTask; use crate::context::AppContext; use crate::model::contested_name::{ContestState, ContestedName}; @@ -60,7 +60,7 @@ pub struct DPNSContestedNamesScreen { voting_identities: Vec, user_identities: Vec, contested_names: Arc>>, - local_dpns_names: Vec<(Identifier, DPNSNameInfo)>, + local_dpns_names: Arc>>, pub app_context: Arc, error_message: Option<(String, MessageType, DateTime)>, sort_column: SortColumn, @@ -83,11 +83,11 @@ impl DPNSContestedNamesScreen { }), DPNSSubscreen::Owned => Vec::new(), })); - let local_dpns_names = match dpns_subscreen { + let local_dpns_names = Arc::new(Mutex::new(match dpns_subscreen { DPNSSubscreen::Active => Vec::new(), DPNSSubscreen::Past => Vec::new(), DPNSSubscreen::Owned => app_context.local_dpns_names().unwrap_or_default(), - }; + })); let voting_identities = app_context .db .get_local_voting_identities(&app_context) @@ -97,10 +97,10 @@ impl DPNSContestedNamesScreen { .get_local_user_identities(&app_context) .unwrap_or_default(); Self { - voting_identities: voting_identities, - user_identities: user_identities, + voting_identities, + user_identities, contested_names, - local_dpns_names: local_dpns_names, + local_dpns_names, app_context: app_context.clone(), error_message: None, sort_column: SortColumn::ContestedName, @@ -184,8 +184,8 @@ impl DPNSContestedNamesScreen { let now = Utc::now(); let elapsed = now.signed_duration_since(*timestamp); - // Automatically dismiss the error message after 5 seconds - if elapsed.num_seconds() > 5 { + // Automatically dismiss the error message after 10 seconds + if elapsed.num_seconds() > 10 { self.dismiss_error(); } } @@ -237,9 +237,23 @@ impl DPNSContestedNamesScreen { ui.label("Please check back later or try refreshing the list."); ui.add_space(20.0); if ui.button("Refresh").clicked() { - app_action |= AppAction::BackendTask(BackendTask::ContestedResourceTask( - ContestedResourceTask::QueryDPNSContestedResources, - )); + if self.refreshing { + app_action |= AppAction::None; + } else { + match self.dpns_subscreen { + DPNSSubscreen::Active | DPNSSubscreen::Past => { + app_action |= + AppAction::BackendTask(BackendTask::ContestedResourceTask( + ContestedResourceTask::QueryDPNSContestedResources, + )); + } + DPNSSubscreen::Owned => { + app_action |= AppAction::BackendTask(BackendTask::IdentityTask( + IdentityTask::RefreshLoadedIdentitiesOwnedDPNSNames, + )); + } + } + } } }); @@ -577,8 +591,12 @@ impl DPNSContestedNamesScreen { } fn render_table_local_dpns_names(&mut self, ui: &mut Ui) { - // Clone and sort a local copy of the `local_dpns_names` vector - let mut sorted_names = self.local_dpns_names.clone(); + let mut sorted_names = { + let dpns_names_guard = self.local_dpns_names.lock().unwrap(); + let dpns_names = dpns_names_guard.clone(); + dpns_names + }; + sorted_names.sort_by(|a, b| match self.sort_column { SortColumn::ContestedName => { let order = a.1.name.cmp(&b.1.name); // Sort by DPNS Name @@ -748,6 +766,7 @@ impl DPNSContestedNamesScreen { impl ScreenLike for DPNSContestedNamesScreen { fn refresh(&mut self) { let mut contested_names = self.contested_names.lock().unwrap(); + let mut dpns_names = self.local_dpns_names.lock().unwrap(); match self.dpns_subscreen { DPNSSubscreen::Active => { *contested_names = self @@ -759,7 +778,7 @@ impl ScreenLike for DPNSContestedNamesScreen { *contested_names = self.app_context.all_contested_names().unwrap_or_default(); } DPNSSubscreen::Owned => { - self.local_dpns_names = self.app_context.local_dpns_names().unwrap_or_default(); + *dpns_names = self.app_context.local_dpns_names().unwrap_or_default(); } } } @@ -780,6 +799,7 @@ impl ScreenLike for DPNSContestedNamesScreen { .into(); let mut contested_names = self.contested_names.lock().unwrap(); + let mut dpns_names = self.local_dpns_names.lock().unwrap(); match self.dpns_subscreen { DPNSSubscreen::Active => { *contested_names = self @@ -791,14 +811,16 @@ impl ScreenLike for DPNSContestedNamesScreen { *contested_names = self.app_context.all_contested_names().unwrap_or_default(); } DPNSSubscreen::Owned => { - self.local_dpns_names = self.app_context.local_dpns_names().unwrap_or_default(); + *dpns_names = self.app_context.local_dpns_names().unwrap_or_default(); } } } fn display_message(&mut self, message: &str, message_type: MessageType) { if message.contains("Finished querying DPNS contested resources") + || message.contains("Successfully refreshed loaded identities dpns names") || message.contains("Contested resource query failed") + || message.contains("Error refreshing owned DPNS names") { self.refreshing = false; } @@ -807,12 +829,21 @@ impl ScreenLike for DPNSContestedNamesScreen { fn ui(&mut self, ctx: &Context) -> AppAction { self.check_error_expiration(); - let mut top_panel_refresh_button = ( - "Refresh", - DesiredAppAction::BackendTask(BackendTask::ContestedResourceTask( - ContestedResourceTask::QueryDPNSContestedResources, - )), - ); + let mut top_panel_refresh_button = match self.dpns_subscreen { + DPNSSubscreen::Active | DPNSSubscreen::Past => ( + "Refresh", + DesiredAppAction::BackendTask(BackendTask::ContestedResourceTask( + ContestedResourceTask::QueryDPNSContestedResources, + )), + ), + DPNSSubscreen::Owned => ( + "Refresh", + DesiredAppAction::BackendTask(BackendTask::IdentityTask( + IdentityTask::RefreshLoadedIdentitiesOwnedDPNSNames, + )), + ), + }; + if self.refreshing { top_panel_refresh_button = ("Refreshing...", DesiredAppAction::None) } @@ -902,6 +933,11 @@ impl ScreenLike for DPNSContestedNamesScreen { let contested_names = self.contested_names.lock().unwrap(); !contested_names.is_empty() }; + // Check if there are any owned dpns names to display + let has_dpns_names = { + let dpns_names = self.local_dpns_names.lock().unwrap(); + !dpns_names.is_empty() + }; // Render the proper table match self.dpns_subscreen { @@ -920,7 +956,7 @@ impl ScreenLike for DPNSContestedNamesScreen { } } DPNSSubscreen::Owned => { - if !self.local_dpns_names.is_empty() { + if has_dpns_names { self.render_table_local_dpns_names(ui); } else { action |= self.render_no_active_contests_or_owned_names(ui); @@ -929,12 +965,19 @@ impl ScreenLike for DPNSContestedNamesScreen { } }); - if action - == AppAction::BackendTask(BackendTask::ContestedResourceTask( + match action { + AppAction::BackendTask(BackendTask::ContestedResourceTask( ContestedResourceTask::QueryDPNSContestedResources, )) - { - self.refreshing = true; + | AppAction::BackendTask(BackendTask::IdentityTask( + IdentityTask::RefreshLoadedIdentitiesOwnedDPNSNames, + )) => { + self.refreshing = true; + } + AppAction::SetMainScreen(_) => { + self.refreshing = false; + } + _ => {} } action