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>,
|
pub hidden_tabs: Vec<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub folders_view_mode: FoldersViewMode,
|
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 {
|
impl Default for LibraryPreferences {
|
||||||
@@ -55,6 +61,7 @@ impl Default for LibraryPreferences {
|
|||||||
],
|
],
|
||||||
hidden_tabs: vec![],
|
hidden_tabs: vec![],
|
||||||
folders_view_mode: FoldersViewMode::Flat,
|
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'",
|
"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 })
|
Ok(Self { conn })
|
||||||
}
|
}
|
||||||
@@ -110,7 +121,7 @@ impl LibraryPreferencesStore {
|
|||||||
let mut stmt = self
|
let mut stmt = self
|
||||||
.conn
|
.conn
|
||||||
.prepare(
|
.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",
|
FROM library_preferences WHERE id = 1",
|
||||||
)
|
)
|
||||||
.map_err(|e| format!("Failed to prepare select: {}", e))?;
|
.map_err(|e| format!("Failed to prepare select: {}", e))?;
|
||||||
@@ -119,6 +130,7 @@ impl LibraryPreferencesStore {
|
|||||||
let tab_order_str: String = row.get(0)?;
|
let tab_order_str: String = row.get(0)?;
|
||||||
let hidden_tabs_str: String = row.get(1)?;
|
let hidden_tabs_str: String = row.get(1)?;
|
||||||
let folders_view_mode_str: Option<String> = row.get(2)?;
|
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)
|
let tab_order: Vec<String> = serde_json::from_str(&tab_order_str)
|
||||||
.unwrap_or_else(|_| LibraryPreferences::default().tab_order);
|
.unwrap_or_else(|_| LibraryPreferences::default().tab_order);
|
||||||
@@ -127,11 +139,17 @@ impl LibraryPreferencesStore {
|
|||||||
let folders_view_mode = folders_view_mode_str
|
let folders_view_mode = folders_view_mode_str
|
||||||
.map(|s| FoldersViewMode::from_str(&s))
|
.map(|s| FoldersViewMode::from_str(&s))
|
||||||
.unwrap_or_default();
|
.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 {
|
Ok(LibraryPreferences {
|
||||||
tab_order,
|
tab_order,
|
||||||
hidden_tabs,
|
hidden_tabs,
|
||||||
folders_view_mode,
|
folders_view_mode,
|
||||||
|
folders_tree_sidebar_width,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -154,18 +172,40 @@ impl LibraryPreferencesStore {
|
|||||||
self.conn
|
self.conn
|
||||||
.execute(
|
.execute(
|
||||||
"INSERT OR REPLACE INTO library_preferences \
|
"INSERT OR REPLACE INTO library_preferences \
|
||||||
(id, tab_order, hidden_tabs, folders_view_mode) \
|
(id, tab_order, hidden_tabs, folders_view_mode, folders_tree_sidebar_width) \
|
||||||
VALUES (1, ?1, ?2, ?3)",
|
VALUES (1, ?1, ?2, ?3, ?4)",
|
||||||
params![
|
params![
|
||||||
tab_order_str,
|
tab_order_str,
|
||||||
hidden_tabs_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))?;
|
.map_err(|e| format!("Failed to save library preferences: {}", e))?;
|
||||||
Ok(prefs)
|
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> {
|
pub fn set_folders_view_mode(&self, mode: FoldersViewMode) -> Result<(), String> {
|
||||||
// Ensure the row exists before updating; if no preferences have been
|
// Ensure the row exists before updating; if no preferences have been
|
||||||
// saved yet, fall back to inserting a default row carrying the new
|
// 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");
|
let loaded = store.get_preferences().expect("load");
|
||||||
assert_eq!(loaded.folders_view_mode, FoldersViewMode::Tree);
|
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