Track space sort keys

This commit is contained in:
2026-05-08 05:40:46 -04:00
parent 820054ff23
commit bce489c119
3 changed files with 93 additions and 4 deletions
+40 -4
View File
@@ -108,8 +108,13 @@ impl BrowserCore {
pub fn new(config: InitialBrowserConfig) -> Result<Self, CoreError> { pub fn new(config: InitialBrowserConfig) -> Result<Self, CoreError> {
let profile = Profile::new(config.profile_name, 0x26251e, ProfileKind::Standard); let profile = Profile::new(config.profile_name, 0x26251e, ProfileKind::Standard);
let active_profile_id = profile.id().clone(); let active_profile_id = profile.id().clone();
let space = let space = Space::new(
Space::new(config.space_name, config.space_icon, 0xf54e00, active_profile_id.clone()); config.space_name,
config.space_icon,
0xf54e00,
active_profile_id.clone(),
0,
);
let new_tab_destination = config.new_tab_destination; let new_tab_destination = config.new_tab_destination;
let new_tab_url = new_tab_destination.url()?; let new_tab_url = new_tab_destination.url()?;
let new_tab_title = tab_title(&new_tab_url); let new_tab_title = tab_title(&new_tab_url);
@@ -163,7 +168,8 @@ impl BrowserCore {
icon: impl Into<String>, icon: impl Into<String>,
accent_hex: u32, accent_hex: u32,
) -> Result<SpaceId, CoreError> { ) -> Result<SpaceId, CoreError> {
let space = Space::new(name, icon, accent_hex, self.active_profile_id.clone()); let sort_key = self.next_space_sort_key();
let space = Space::new(name, icon, accent_hex, self.active_profile_id.clone(), sort_key);
let space_id = space.id().clone(); let space_id = space.id().clone();
let default_profile_id = space.default_profile_id().clone(); let default_profile_id = space.default_profile_id().clone();
let tab = self.build_tab_for(space_id.clone(), default_profile_id, self.new_tab_url()?); let tab = self.build_tab_for(space_id.clone(), default_profile_id, self.new_tab_url()?);
@@ -266,6 +272,20 @@ impl BrowserCore {
Ok(()) Ok(())
} }
pub fn set_space_sort_key(
&mut self,
space_id: &SpaceId,
sort_key: u64,
) -> Result<(), CoreError> {
let space = self
.spaces
.iter_mut()
.find(|space| space.id() == space_id)
.ok_or_else(|| CoreError::SpaceNotFound { id: space_id.clone() })?;
space.set_sort_key(sort_key);
Ok(())
}
pub fn set_search_engine(&mut self, search_engine: SearchEngine) { pub fn set_search_engine(&mut self, search_engine: SearchEngine) {
self.search_engine = search_engine; self.search_engine = search_engine;
} }
@@ -329,7 +349,7 @@ impl BrowserCore {
split_layouts: self.visible_split_layouts(), split_layouts: self.visible_split_layouts(),
installed_plugins: self.installed_plugins.clone(), installed_plugins: self.installed_plugins.clone(),
plugin_audit_events: self.plugin_audit_events.clone(), plugin_audit_events: self.plugin_audit_events.clone(),
spaces: self.spaces.clone(), spaces: self.sorted_spaces(),
profiles: self.profiles.clone(), profiles: self.profiles.clone(),
sync_status: self.sync_status(), sync_status: self.sync_status(),
tabs: self.visible_tabs(), tabs: self.visible_tabs(),
@@ -365,6 +385,22 @@ impl BrowserCore {
.ok_or_else(|| CoreError::SpaceNotFound { id: self.active_space_id.clone() }) .ok_or_else(|| CoreError::SpaceNotFound { id: self.active_space_id.clone() })
} }
fn next_space_sort_key(&self) -> u64 {
self.spaces
.iter()
.map(Space::sort_key)
.max()
.map_or(0, |sort_key| sort_key.saturating_add(1))
}
fn sorted_spaces(&self) -> Vec<Space> {
let mut spaces = self.spaces.clone();
spaces.sort_by(|left, right| {
left.sort_key().cmp(&right.sort_key()).then_with(|| left.id().cmp(right.id()))
});
spaces
}
fn favorites(&self) -> Vec<BrowserTab> { fn favorites(&self) -> Vec<BrowserTab> {
self.tabs.iter().filter(|tab| tab.flags().favorite).cloned().collect() self.tabs.iter().filter(|tab| tab.flags().favorite).cloned().collect()
} }
+40
View File
@@ -51,6 +51,42 @@ fn created_space_uses_default_sidebar_width() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
#[test]
fn created_spaces_receive_incrementing_sort_keys() -> Result<(), Box<dyn Error>> {
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
let work_space_id = core.snapshot()?.active_space_id;
let research_space_id = core.create_space("Research", "R", 0x9fc9a2)?;
let snapshot = core.snapshot()?;
let Some(work_space) = snapshot.spaces.iter().find(|space| space.id() == &work_space_id) else {
return Err("missing work space".into());
};
let Some(research_space) =
snapshot.spaces.iter().find(|space| space.id() == &research_space_id)
else {
return Err("missing research space".into());
};
assert_eq!(work_space.sort_key(), 0);
assert_eq!(research_space.sort_key(), 1);
Ok(())
}
#[test]
fn snapshot_orders_spaces_by_sort_key() -> Result<(), Box<dyn Error>> {
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
let work_space_id = core.snapshot()?.active_space_id;
let research_space_id = core.create_space("Research", "R", 0x9fc9a2)?;
core.set_space_sort_key(&work_space_id, 20)?;
core.set_space_sort_key(&research_space_id, 10)?;
let snapshot = core.snapshot()?;
assert_eq!(snapshot.spaces[0].id(), &research_space_id);
assert_eq!(snapshot.spaces[1].id(), &work_space_id);
Ok(())
}
#[test] #[test]
fn space_default_profile_updates_with_profile_validation() -> Result<(), Box<dyn Error>> { fn space_default_profile_updates_with_profile_validation() -> Result<(), Box<dyn Error>> {
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?; let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
@@ -91,9 +127,13 @@ fn space_settings_refresh_updated_at() -> Result<(), Box<dyn Error>> {
core.set_space_sidebar_width(&work_space_id, 320)?; core.set_space_sidebar_width(&work_space_id, 320)?;
let after_sidebar_update = active_space_updated_at(&core, &work_space_id)?; let after_sidebar_update = active_space_updated_at(&core, &work_space_id)?;
core.set_space_sort_key(&work_space_id, 8)?;
let after_sort_update = active_space_updated_at(&core, &work_space_id)?;
assert!(after_archive_update > before_update); assert!(after_archive_update > before_update);
assert!(after_profile_update > after_archive_update); assert!(after_profile_update > after_archive_update);
assert!(after_sidebar_update > after_profile_update); assert!(after_sidebar_update > after_profile_update);
assert!(after_sort_update > after_sidebar_update);
Ok(()) Ok(())
} }
+13
View File
@@ -19,6 +19,7 @@ pub struct Space {
default_profile_id: ProfileId, default_profile_id: ProfileId,
archive_policy: ArchivePolicy, archive_policy: ArchivePolicy,
sidebar_width_px: u16, sidebar_width_px: u16,
sort_key: u64,
created_at: SystemTime, created_at: SystemTime,
updated_at: SystemTime, updated_at: SystemTime,
} }
@@ -30,6 +31,7 @@ impl Space {
icon: impl Into<String>, icon: impl Into<String>,
accent_hex: u32, accent_hex: u32,
default_profile_id: ProfileId, default_profile_id: ProfileId,
sort_key: u64,
) -> Self { ) -> Self {
let created_at = SystemTime::now(); let created_at = SystemTime::now();
Self { Self {
@@ -40,6 +42,7 @@ impl Space {
default_profile_id, default_profile_id,
archive_policy: ArchivePolicy::Manual, archive_policy: ArchivePolicy::Manual,
sidebar_width_px: DEFAULT_SIDEBAR_WIDTH_PX, sidebar_width_px: DEFAULT_SIDEBAR_WIDTH_PX,
sort_key,
created_at, created_at,
updated_at: created_at, updated_at: created_at,
} }
@@ -95,6 +98,16 @@ impl Space {
self.record_update(); self.record_update();
} }
#[must_use]
pub fn sort_key(&self) -> u64 {
self.sort_key
}
pub fn set_sort_key(&mut self, sort_key: u64) {
self.sort_key = sort_key;
self.record_update();
}
#[must_use] #[must_use]
pub fn created_at(&self) -> SystemTime { pub fn created_at(&self) -> SystemTime {
self.created_at self.created_at