diff --git a/src/database/identities.rs b/src/database/identities.rs index 79d88b172..0c63044b5 100644 --- a/src/database/identities.rs +++ b/src/database/identities.rs @@ -285,8 +285,8 @@ impl Database { "CREATE TABLE IF NOT EXISTS identity_order ( pos INTEGER NOT NULL, identity_id BLOB NOT NULL, - PRIMARY KEY(pos) - )", + PRIMARY KEY(pos), + FOREIGN KEY (identity_id) REFERENCES identity(id) ON DELETE CASCADE)", )?; Ok(()) } @@ -317,33 +317,54 @@ impl Database { Ok(()) } - /// Loads the custom identity order from the DB, returning a list of Identifiers in the stored order. - /// If there's no data, returns an empty Vec. + /// Loads the user’s custom identity order (the entire list). + /// If an identity in the order doesn't exist in the identity table, it is removed. pub fn load_identity_order(&self) -> rusqlite::Result> { - // Make sure table exists (in case it doesn't) + // Make sure table exists self.ensure_identity_order_table_exists()?; let conn = self.conn.lock().unwrap(); // Read all rows sorted by pos - let mut stmt = conn.prepare( - "SELECT identity_id FROM identity_order - ORDER BY pos ASC", - )?; + let mut stmt = conn.prepare("SELECT identity_id FROM identity_order ORDER BY pos ASC")?; let mut rows = stmt.query([])?; - let mut result = Vec::new(); + let mut final_list = Vec::new(); + let mut to_remove = Vec::new(); while let Some(row) = rows.next()? { let id_bytes: Vec = row.get(0)?; // Convert from raw bytes to an Identifier - if let Ok(identifier) = Identifier::from_vec(id_bytes) { - result.push(identifier); + let identifier = match Identifier::from_vec(id_bytes.clone()) { + Ok(id) => id, + Err(_) => { + // If parsing as an Identifier fails, queue for removal + to_remove.push(id_bytes); + continue; + } + }; + + // Check if the identity is still in 'identity' table + let mut check_stmt = + conn.prepare("SELECT EXISTS(SELECT 1 FROM identity WHERE id = ?)")?; + let exists: i64 = check_stmt.query_row(params![identifier.to_vec()], |r| r.get(0))?; + if exists == 1 { + // Keep it + final_list.push(identifier); } else { - // If for some reason it fails to parse, skip it or handle error + // Queue for removal because it doesn't exist in the identity table + to_remove.push(identifier.to_vec()); } } - Ok(result) + // Remove any “dangling” references + for id in to_remove { + conn.execute( + "DELETE FROM identity_order WHERE identity_id = ?", + params![id], + )?; + } + + Ok(final_list) } } diff --git a/src/ui/identities/identities_screen.rs b/src/ui/identities/identities_screen.rs index cd759a3c5..6815df576 100644 --- a/src/ui/identities/identities_screen.rs +++ b/src/ui/identities/identities_screen.rs @@ -30,7 +30,7 @@ use eframe::egui::{self, Context}; use eframe::emath::Align; use egui::{Color32, Frame, Margin, RichText, Ui}; use egui_extras::{Column, TableBuilder}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; @@ -101,12 +101,34 @@ impl IdentitiesScreen { screen } - /// Reorder the underlying IndexMap to match a list of IDs + /// Reorders `self.identities` to match the order of the provided list of IDs. + /// Any IDs not present in the provided list are left in their current position. fn reorder_map_to(&self, new_order: Vec) { let mut lock = self.identities.lock().unwrap(); - for (desired_idx, id) in new_order.iter().enumerate() { - if let Some(current_idx) = lock.get_index_of(id) { - if current_idx != desired_idx && current_idx < lock.len() { + if lock.is_empty() || new_order.is_empty() { + return; + } + + // 1) Collect the set of IDs currently in `self.identities` + let existing_ids: HashSet = lock.keys().cloned().collect(); + + // 2) Build a filtered list that only includes IDs which exist in the map + // (and also limit the length so we don’t swap out of range) + let valid_ids: Vec = new_order + .into_iter() + .filter(|id| existing_ids.contains(id)) + .take(lock.len()) // never try to reorder more items than we actually have + .collect(); + + // 3) Do the swaps only for items still present in our map, + // skipping any that no longer exist or where desired_idx is out of range + for (desired_idx, id) in valid_ids.into_iter().enumerate() { + // Double‐check the desired index is in range + if desired_idx >= lock.len() { + break; + } + if let Some(current_idx) = lock.get_index_of(&id) { + if current_idx != desired_idx { lock.swap_indices(current_idx, desired_idx); } }