Skip to content

Commit 3917331

Browse files
committed
fix(ui): prevent duplicate contracts from causing double-expand in chooser
When a system contract (e.g. DPNS) was also added manually by the user, get_contracts() returned it twice: once from the DB (shown by Base58 ID) and once as the injected system contract (shown by alias). Both entries shared the same contract ID, so clicking either one toggled the same key in expanded_contracts, causing both to expand simultaneously. Fix: deduplicate DB contracts against system contract IDs before merging. Also wrap each contract's header + content in push_id for egui ID safety.
1 parent 1b71f02 commit 3917331

3 files changed

Lines changed: 46 additions & 49 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ test_db*
3434
**/.DS_Store
3535
explorer.log
3636
.gitaipconfig
37+
worktrees/

src/context/contract_token_db.rs

Lines changed: 39 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::model::wallet::WalletSeedHash;
44
use crate::ui::tokens::tokens_screen::{IdentityTokenBalance, IdentityTokenIdentifier};
55
use bincode::config;
66
use dash_sdk::dpp::data_contract::TokenConfiguration;
7+
use dash_sdk::dpp::data_contract::accessors::v0::DataContractV0Getters;
78
use dash_sdk::platform::{DataContract, Identifier};
89
use dash_sdk::query_types::IndexMap;
910
use rusqlite::Result;
@@ -20,52 +21,44 @@ impl AppContext {
2021
// Get contracts from the database
2122
let mut contracts = self.db.get_contracts(self, limit, offset)?;
2223

23-
// Add the DPNS contract to the list
24-
let dpns_contract = QualifiedContract {
25-
contract: Arc::clone(&self.dpns_contract).as_ref().clone(),
26-
alias: Some("dpns".to_string()),
27-
};
28-
29-
// Insert the DPNS contract at 0
30-
contracts.insert(0, dpns_contract);
31-
32-
// Add the token history contract to the list
33-
let token_history_contract = QualifiedContract {
34-
contract: Arc::clone(&self.token_history_contract).as_ref().clone(),
35-
alias: Some("token_history".to_string()),
36-
};
37-
38-
// Insert the token history contract at 1
39-
contracts.insert(1, token_history_contract);
40-
41-
// Add the withdrawal contract to the list
42-
let withdraws_contract = QualifiedContract {
43-
contract: Arc::clone(&self.withdraws_contract).as_ref().clone(),
44-
alias: Some("withdrawals".to_string()),
45-
};
46-
47-
// Insert the withdrawal contract at 2
48-
contracts.insert(2, withdraws_contract);
49-
50-
// Add the keyword search contract to the list
51-
let keyword_search_contract = QualifiedContract {
52-
contract: Arc::clone(&self.keyword_search_contract).as_ref().clone(),
53-
alias: Some("keyword_search".to_string()),
54-
};
55-
56-
// Insert the keyword search contract at 3
57-
contracts.insert(3, keyword_search_contract);
58-
59-
// Add the DashPay contract to the list
60-
let dashpay_contract = QualifiedContract {
61-
contract: Arc::clone(&self.dashpay_contract).as_ref().clone(),
62-
alias: Some("dashpay".to_string()),
63-
};
64-
65-
// Insert the DashPay contract at 4
66-
contracts.insert(4, dashpay_contract);
67-
68-
Ok(contracts)
24+
// Build the list of system contracts to prepend
25+
let system_contracts = vec![
26+
QualifiedContract {
27+
contract: Arc::clone(&self.dpns_contract).as_ref().clone(),
28+
alias: Some("dpns".to_string()),
29+
},
30+
QualifiedContract {
31+
contract: Arc::clone(&self.token_history_contract).as_ref().clone(),
32+
alias: Some("token_history".to_string()),
33+
},
34+
QualifiedContract {
35+
contract: Arc::clone(&self.withdraws_contract).as_ref().clone(),
36+
alias: Some("withdrawals".to_string()),
37+
},
38+
QualifiedContract {
39+
contract: Arc::clone(&self.keyword_search_contract).as_ref().clone(),
40+
alias: Some("keyword_search".to_string()),
41+
},
42+
QualifiedContract {
43+
contract: Arc::clone(&self.dashpay_contract).as_ref().clone(),
44+
alias: Some("dashpay".to_string()),
45+
},
46+
];
47+
48+
// Collect system contract IDs to deduplicate DB contracts that match
49+
let system_ids: std::collections::HashSet<_> = system_contracts
50+
.iter()
51+
.map(|c| c.contract.id())
52+
.collect();
53+
54+
// Remove any DB contracts that duplicate a system contract
55+
contracts.retain(|c| !system_ids.contains(&c.contract.id()));
56+
57+
// Prepend system contracts in order
58+
let mut result = system_contracts;
59+
result.append(&mut contracts);
60+
61+
Ok(result)
6962
}
7063

7164
pub fn get_contract_by_id(

src/ui/components/contract_chooser_panel.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,10 @@ pub fn add_contract_chooser_panel(
234234
// Check if this contract is expanded
235235
let is_expanded = chooser_state.expanded_contracts.contains(&contract_id);
236236

237+
// Wrap entire contract (header + content) in push_id to avoid
238+
// egui widget ID collisions between different contracts' +/- buttons
239+
ui.push_id(&contract_id, |ui| {
240+
237241
// Render the custom collapsing header for the contract
238242
if render_collapsing_header(ui, &display_name, is_expanded, is_selected_contract, 0) {
239243
if is_expanded {
@@ -245,7 +249,6 @@ pub fn add_contract_chooser_panel(
245249

246250
// Show contract content if expanded
247251
if is_expanded {
248-
ui.push_id(&contract_id, |ui| {
249252
ui.vertical(|ui| {
250253
//
251254
// ===== Document Types Section =====
@@ -533,8 +536,8 @@ pub fn add_contract_chooser_panel(
533536
);
534537
}
535538
});
536-
});
537-
}
539+
}
540+
}); // Close push_id for contract
538541
}
539542
});
540543
});

0 commit comments

Comments
 (0)