fix(app-settings): persist and prune pinned tree nodes

This commit is contained in:
t8y2 committed 2026-05-22 14:28:40 +08:00
1 parent 0e189ba0cc
commit 1ef09e8975
12 files changed
+237 -2

No files matched your search

+41
View File
@@ -340,6 +340,24 @@ impl Storage {
.unwrap_or_else(|| DesktopSettings::default().show_tray_icon),
})
}
pub async fn save_pinned_tree_node_ids(&self, ids: &[String]) -> Result<(), String> {
let mut settings = self.load_app_settings_json().await?;
let values = ids.iter().map(|id| serde_json::Value::String(id.clone())).collect::<Vec<serde_json::Value>>();
settings.insert("pinned_tree_node_ids".to_string(), serde_json::Value::Array(values));
self.save_app_settings_json(&settings).await
}
pub async fn load_pinned_tree_node_ids(&self) -> Result<Vec<String>, String> {
let settings = self.load_app_settings_json().await?;
let Some(value) = settings.get("pinned_tree_node_ids") else {
return Ok(Vec::new());
};
let Some(array) = value.as_array() else {
return Ok(Vec::new());
};
Ok(array.iter().filter_map(|item| item.as_str().map(|value| value.to_string())).collect())
}
}
// ---------------------------------------------------------------------------
@@ -1041,4 +1059,27 @@ mod tests {
assert_eq!(storage.load_password_hash().await.unwrap(), Some("hash-2".to_string()));
assert_eq!(storage.load_desktop_settings().await.unwrap(), DesktopSettings { show_tray_icon: false });
}
#[tokio::test]
async fn pinned_tree_node_ids_default_to_empty() {
let path = temp_db_path("pinned-tree-default");
let storage = Storage::open(&path).await.unwrap();
assert_eq!(storage.load_pinned_tree_node_ids().await.unwrap(), Vec::<String>::new());
}
#[tokio::test]
async fn pinned_tree_node_ids_roundtrip_and_preserve_password_hash() {
let path = temp_db_path("pinned-tree-roundtrip");
let storage = Storage::open(&path).await.unwrap();
storage.save_password_hash("hash-3").await.unwrap();
storage.save_pinned_tree_node_ids(&["conn-1".to_string(), "conn-1:db:main".to_string()]).await.unwrap();
assert_eq!(
storage.load_pinned_tree_node_ids().await.unwrap(),
vec!["conn-1".to_string(), "conn-1:db:main".to_string()]
);
assert_eq!(storage.load_password_hash().await.unwrap(), Some("hash-3".to_string()));
}
}
+5
View File
@@ -195,6 +195,11 @@ async fn main() {
.route("/update/check", get(routes::update::check_for_updates))
// Layout
.route("/layout/sidebar", post(routes::layout::save_sidebar_layout).get(routes::layout::load_sidebar_layout))
// App settings
.route(
"/app-settings/pinned-tree-node-ids",
get(routes::app_settings::load_pinned_tree_node_ids).post(routes::app_settings::save_pinned_tree_node_ids),
)
.layer(middleware::from_fn_with_state(web_state.clone(), auth::auth_middleware))
.with_state(web_state.clone());
+27
View File
@@ -0,0 +1,27 @@
use std::sync::Arc;
use axum::extract::State;
use axum::Json;
use serde::Deserialize;
use crate::error::AppError;
use crate::state::WebState;
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SavePinnedTreeNodeIdsRequest {
pub ids: Vec<String>,
}
pub async fn load_pinned_tree_node_ids(State(state): State<Arc<WebState>>) -> Result<Json<Vec<String>>, AppError> {
let ids = state.app.storage.load_pinned_tree_node_ids().await.map_err(AppError)?;
Ok(Json(ids))
}
pub async fn save_pinned_tree_node_ids(
State(state): State<Arc<WebState>>,
Json(body): Json<SavePinnedTreeNodeIdsRequest>,
) -> Result<Json<()>, AppError> {
state.app.storage.save_pinned_tree_node_ids(&body.ids).await.map_err(AppError)?;
Ok(Json(()))
}
+1
View File
@@ -1,5 +1,6 @@
pub mod agents;
pub mod ai;
pub mod app_settings;
pub mod connection;
pub mod database_export;
pub mod history;