use crate::ui::layout::SplitNode;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AppState {
pub pane_projects: HashMap<usize, String>,
pub selected_columns: HashMap<usize, usize>,
pub selected_task_indices: HashMap<usize, usize>,
pub focused_pane: usize,
}
impl Default for AppState {
fn default() -> Self {
Self {
pane_projects: HashMap::new(),
selected_columns: HashMap::new(),
selected_task_indices: HashMap::new(),
focused_pane: 0,
}
}
}
fn get_state_file_path() -> PathBuf {
let home = std::env::var("HOME").expect("HOME environment variable not set");
PathBuf::from(home).join(".kanban").join("state.json")
}
pub fn extract_state(app: &crate::app::App) -> AppState {
use crate::ui::layout::SplitNode;
let mut state = AppState {
focused_pane: app.focused_pane,
selected_columns: app.selected_column.clone(),
selected_task_indices: app.selected_task_index.clone(),
..Default::default()
};
collect_pane_projects(&app.split_tree, &mut state.pane_projects);
state
}
fn collect_pane_projects(node: &SplitNode, pane_projects: &mut HashMap<usize, String>) {
match node {
SplitNode::Leaf { id, project_id } => {
if let Some(pid) = project_id {
pane_projects.insert(*id, pid.clone());
}
}
SplitNode::Horizontal { left, right, .. } => {
collect_pane_projects(left, pane_projects);
collect_pane_projects(right, pane_projects);
}
SplitNode::Vertical { top, bottom, .. } => {
collect_pane_projects(top, pane_projects);
collect_pane_projects(bottom, pane_projects);
}
}
}
pub fn save_state(state: &AppState) -> Result<()> {
let state_path = get_state_file_path();
if let Some(parent) = state_path.parent() {
std::fs::create_dir_all(parent)?;
}
let json = serde_json::to_string_pretty(state)?;
std::fs::write(state_path, json)?;
Ok(())
}
pub fn load_state() -> Result<AppState> {
let state_path = get_state_file_path();
if !state_path.exists() {
return Ok(AppState::default());
}
let content = std::fs::read_to_string(state_path)?;
let state: AppState = serde_json::from_str(&content)?;
Ok(state)
}
pub fn apply_state(app: &mut crate::app::App, state: AppState) {
use crate::ui::layout::SplitNode;
for (pane_id, project_name) in state.pane_projects {
if let Some(SplitNode::Leaf { project_id, .. }) = app.split_tree.find_pane_mut(pane_id) {
if let Some(project) = app.projects.iter().find(|p| p.name == project_name) {
let project_path = project.path.clone();
let project_type = project.project_type;
if let Ok(reloaded_project) = crate::fs::load_project_with_type(&project_path, project_type) {
if let Some(idx) = app.projects.iter().position(|p| p.name == project_name) {
app.projects[idx] = reloaded_project;
}
*project_id = Some(project_name);
}
}
}
}
app.selected_column = state.selected_columns;
app.selected_task_index = state.selected_task_indices;
let all_panes = app.split_tree.collect_pane_ids();
if all_panes.contains(&state.focused_pane) {
app.focused_pane = state.focused_pane;
}
}