feat(library-prefs): persist folders-tree sidebar width
New i64 field on LibraryPreferences for the tree-mode sidebar width the user chooses via drag. Migration adds the column non-destructively; NULL means "use default". cc
This commit is contained in:
1 file changed
+92
-4
@@ -38,6 +38,12 @@ pub struct LibraryPreferences {
|
||||
pub hidden_tabs: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub folders_view_mode: FoldersViewMode,
|
||||
/// Width (in CSS pixels) of the tree-mode left sidebar in the Folders
|
||||
/// tab. `None` means "use the frontend default" (currently 432px).
|
||||
/// Persisted on drag-end so the choice survives restarts. Bounds are
|
||||
/// enforced on the frontend (min 200px, max 40% of content area).
|
||||
#[serde(default)]
|
||||
pub folders_tree_sidebar_width: Option<u32>,
|
||||
}
|
||||
|
||||
impl Default for LibraryPreferences {
|
||||
@@ -55,6 +61,7 @@ impl Default for LibraryPreferences {
|
||||
],
|
||||
hidden_tabs: vec![],
|
||||
folders_view_mode: FoldersViewMode::Flat,
|
||||
folders_tree_sidebar_width: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,6 +98,10 @@ impl LibraryPreferencesStore {
|
||||
"ALTER TABLE library_preferences ADD COLUMN folders_view_mode TEXT DEFAULT 'flat'",
|
||||
[],
|
||||
);
|
||||
let _ = conn.execute(
|
||||
"ALTER TABLE library_preferences ADD COLUMN folders_tree_sidebar_width INTEGER DEFAULT NULL",
|
||||
[],
|
||||
);
|
||||
|
||||
Ok(Self { conn })
|
||||
}
|
||||
@@ -110,7 +121,7 @@ impl LibraryPreferencesStore {
|
||||
let mut stmt = self
|
||||
.conn
|
||||
.prepare(
|
||||
"SELECT tab_order, hidden_tabs, folders_view_mode \
|
||||
"SELECT tab_order, hidden_tabs, folders_view_mode, folders_tree_sidebar_width \
|
||||
FROM library_preferences WHERE id = 1",
|
||||
)
|
||||
.map_err(|e| format!("Failed to prepare select: {}", e))?;
|
||||
@@ -119,6 +130,7 @@ impl LibraryPreferencesStore {
|
||||
let tab_order_str: String = row.get(0)?;
|
||||
let hidden_tabs_str: String = row.get(1)?;
|
||||
let folders_view_mode_str: Option<String> = row.get(2)?;
|
||||
let folders_tree_sidebar_width: Option<i64> = row.get(3)?;
|
||||
|
||||
let tab_order: Vec<String> = serde_json::from_str(&tab_order_str)
|
||||
.unwrap_or_else(|_| LibraryPreferences::default().tab_order);
|
||||
@@ -127,11 +139,17 @@ impl LibraryPreferencesStore {
|
||||
let folders_view_mode = folders_view_mode_str
|
||||
.map(|s| FoldersViewMode::from_str(&s))
|
||||
.unwrap_or_default();
|
||||
// Stored as INTEGER in SQLite; clamp to non-negative on read.
|
||||
// Frontend re-clamps against the live max anyway.
|
||||
let folders_tree_sidebar_width = folders_tree_sidebar_width
|
||||
.filter(|v| *v > 0)
|
||||
.map(|v| v as u32);
|
||||
|
||||
Ok(LibraryPreferences {
|
||||
tab_order,
|
||||
hidden_tabs,
|
||||
folders_view_mode,
|
||||
folders_tree_sidebar_width,
|
||||
})
|
||||
});
|
||||
|
||||
@@ -154,18 +172,40 @@ impl LibraryPreferencesStore {
|
||||
self.conn
|
||||
.execute(
|
||||
"INSERT OR REPLACE INTO library_preferences \
|
||||
(id, tab_order, hidden_tabs, folders_view_mode) \
|
||||
VALUES (1, ?1, ?2, ?3)",
|
||||
(id, tab_order, hidden_tabs, folders_view_mode, folders_tree_sidebar_width) \
|
||||
VALUES (1, ?1, ?2, ?3, ?4)",
|
||||
params![
|
||||
tab_order_str,
|
||||
hidden_tabs_str,
|
||||
prefs.folders_view_mode.as_str()
|
||||
prefs.folders_view_mode.as_str(),
|
||||
prefs.folders_tree_sidebar_width.map(|v| v as i64),
|
||||
],
|
||||
)
|
||||
.map_err(|e| format!("Failed to save library preferences: {}", e))?;
|
||||
Ok(prefs)
|
||||
}
|
||||
|
||||
pub fn set_folders_tree_sidebar_width(&self, width: u32) -> Result<(), String> {
|
||||
// Same upsert pattern as `set_folders_view_mode`: try the cheap
|
||||
// UPDATE first; if no row exists yet, fall back to inserting a
|
||||
// default row carrying just the new width.
|
||||
let updated = self
|
||||
.conn
|
||||
.execute(
|
||||
"UPDATE library_preferences SET folders_tree_sidebar_width = ?1 WHERE id = 1",
|
||||
params![width as i64],
|
||||
)
|
||||
.map_err(|e| format!("Failed to set folders_tree_sidebar_width: {}", e))?;
|
||||
|
||||
if updated == 0 {
|
||||
let mut prefs = LibraryPreferences::default();
|
||||
prefs.folders_tree_sidebar_width = Some(width);
|
||||
self.save_preferences(prefs)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_folders_view_mode(&self, mode: FoldersViewMode) -> Result<(), String> {
|
||||
// Ensure the row exists before updating; if no preferences have been
|
||||
// saved yet, fall back to inserting a default row carrying the new
|
||||
@@ -350,4 +390,52 @@ mod tests {
|
||||
let loaded = store.get_preferences().expect("load");
|
||||
assert_eq!(loaded.folders_view_mode, FoldersViewMode::Tree);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fresh_store_returns_none_folders_tree_sidebar_width() {
|
||||
let (_dir, store) = fresh_store();
|
||||
let prefs = store.get_preferences().expect("get prefs");
|
||||
assert_eq!(prefs.folders_tree_sidebar_width, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_folders_tree_sidebar_width_persists_on_empty_table() {
|
||||
let (_dir, store) = fresh_store();
|
||||
store
|
||||
.set_folders_tree_sidebar_width(512)
|
||||
.expect("set width");
|
||||
let prefs = store.get_preferences().expect("get prefs");
|
||||
assert_eq!(prefs.folders_tree_sidebar_width, Some(512));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_folders_tree_sidebar_width_updates_existing_row() {
|
||||
let (_dir, store) = fresh_store();
|
||||
|
||||
let mut seeded = LibraryPreferences::default();
|
||||
seeded.tab_order = vec!["tracks".into(), "folders".into()];
|
||||
store.save_preferences(seeded).expect("save seed");
|
||||
|
||||
store
|
||||
.set_folders_tree_sidebar_width(640)
|
||||
.expect("set width");
|
||||
let after = store.get_preferences().expect("get prefs");
|
||||
assert_eq!(after.folders_tree_sidebar_width, Some(640));
|
||||
assert_eq!(
|
||||
after.tab_order,
|
||||
vec!["tracks".to_string(), "folders".to_string()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_json_without_sidebar_width_defaults_to_none() {
|
||||
let legacy = r#"{
|
||||
"tab_order": ["tracks", "folders", "albums", "artists"],
|
||||
"hidden_tabs": [],
|
||||
"folders_view_mode": "tree"
|
||||
}"#;
|
||||
let prefs: LibraryPreferences =
|
||||
serde_json::from_str(legacy).expect("parse legacy json");
|
||||
assert_eq!(prefs.folders_tree_sidebar_width, None);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user