use std::path::Path;
use crate::actions::AppAction;
use crate::app::{App, Screen};
use crate::domain::{SortMode, TodoService};
use crate::error::AppError;
pub async fn execute(
app: &mut App,
action: AppAction,
service: &TodoService,
exe_dir: &Path,
) -> Result<(), AppError> {
match action {
AppAction::Quit => {
app.should_quit = true;
}
AppAction::SelectTodo(index) => {
if index < app.todos.len() {
app.list_state.select(Some(index));
app.selected_id = app.todos.get(index).map(|t| t.id.clone());
}
}
AppAction::SelectPrevious => {
app.select_previous();
}
AppAction::SelectNext => {
app.select_next();
}
AppAction::OpenDetail(id) => {
app.selected_id = Some(id.clone());
app.transition_to(Screen::Detail);
if let Ok(todo) = service.get(&id).await {
let todo_id = todo.id.clone();
if let Some(idx) = app.todos.iter().position(|t| t.id == todo_id) {
app.todos[idx] = todo;
}
}
}
AppAction::EditTodo(id) => {
if let Some(todo) = app.todos.iter().find(|t| t.id == id) {
let mut content = todo.title.clone();
if !todo.note.is_empty() {
content.push('\n');
content.push_str(&todo.note);
}
app.init_note_editor(content);
app.transition_to(Screen::NoteEditor);
}
}
AppAction::CreateTodoStart => {
app.init_create_todo_editor();
app.transition_to(Screen::CreateTodo);
}
AppAction::CreateTodo(title, note) => {
if let Ok(todo) = service.create(&title, ¬e).await {
reload_todos(app, service).await;
if let Some(idx) = app.todos.iter().position(|t| t.id == todo.id) {
app.list_state.select(Some(idx));
app.selected_id = Some(todo.id);
}
app.screen = Screen::List;
app.previous_screen = None;
app.create_todo_editor = None;
}
}
AppAction::ToggleTodo(id) => {
app.selected_id = Some(id.clone());
if let Some(idx) = app.todos.iter().position(|t| t.id == id) {
app.list_state.select(Some(idx));
}
if let Err(e) = service.toggle(&id).await {
app.error_message = Some(e.to_string());
} else {
reload_todos(app, service).await;
}
}
AppAction::ShowDeleteConfirm => {
app.transition_to(Screen::DeleteConfirm);
}
AppAction::CloseDeleteConfirm => {
app.screen = app.previous_screen.take().unwrap_or(Screen::List);
}
AppAction::DeleteTodo(id) => {
if let Err(e) = service.delete(&id).await {
app.error_message = Some(e.to_string());
} else {
reload_todos(app, service).await;
app.screen = Screen::List;
app.previous_screen = None;
}
}
AppAction::SaveTodo(id, title, note) => {
if let Err(e) = service.update(&id, &title, ¬e).await {
app.error_message = Some(e.to_string());
} else {
reload_todos(app, service).await;
app.screen = Screen::Detail;
app.note_editor = None;
}
}
AppAction::SetPriority(id, priority) => {
app.selected_id = Some(id.clone());
if let Some(idx) = app.todos.iter().position(|t| t.id == id) {
app.list_state.select(Some(idx));
}
if let Err(e) = service.set_priority(&id, &priority).await {
app.error_message = Some(e.to_string());
} else {
let prev_id = Some(id);
reload_todos(app, service).await;
if let Some(ref prev) = prev_id {
if let Some(idx) = app.todos.iter().position(|t| t.id == *prev) {
app.list_state.select(Some(idx));
}
}
}
}
AppAction::CloseDetail => {
app.screen = Screen::List;
app.previous_screen = None;
}
AppAction::CloseNoteEditor => {
app.screen = Screen::Detail;
app.note_editor = None;
}
AppAction::CloseCreateTodo => {
app.screen = Screen::List;
app.previous_screen = None;
app.create_todo_editor = None;
}
AppAction::ToggleSortMode => {
app.sort_mode.toggle();
save_config(exe_dir, app.sort_mode);
reload_todos(app, service).await;
}
}
Ok(())
}
pub async fn reload_todos(app: &mut App, service: &TodoService) {
let prev_id = app.selected_id.clone();
match service.list_active_sorted(app.sort_mode).await {
Ok(todos) => {
app.todos = todos;
if !app.todos.is_empty() {
if let Some(ref id) = prev_id {
if let Some(idx) = app.todos.iter().position(|t| t.id == *id) {
app.list_state.select(Some(idx));
app.selected_id = prev_id;
} else {
app.list_state.select(Some(0));
app.selected_id = app.todos.first().map(|t| t.id.clone());
}
} else {
app.list_state.select(Some(0));
app.selected_id = app.todos.first().map(|t| t.id.clone());
}
}
app.error_message = None;
}
Err(e) => {
app.error_message = Some(e.to_string());
}
}
}
pub fn save_config(exe_dir: &Path, sort_mode: SortMode) {
let config_path = exe_dir.join(".todo").join("config.json");
if let Some(parent) = config_path.parent() {
if let Err(err) = std::fs::create_dir_all(parent) {
eprintln!(
"Warning: failed to create config directory {}: {}",
parent.display(),
err
);
return;
}
}
let json = serde_json::json!({
"sort_mode": sort_mode.label()
});
let content = match serde_json::to_string_pretty(&json) {
Ok(content) => content,
Err(err) => {
eprintln!(
"Warning: failed to serialize config {}: {}",
config_path.display(),
err
);
return;
}
};
if let Err(err) = std::fs::write(&config_path, content) {
eprintln!(
"Warning: failed to write config {}: {}",
config_path.display(),
err
);
}
}