aven 0.1.9

Local-first task manager CLI and sync server
Documentation
use anyhow::Result;
use tokio::task::JoinHandle;

use crate::config::TaskIntakeConfig;
use crate::operations::{
    TaskDraft, add_note as add_note_operation, create_task as create_task_operation,
};
use crate::refs::display_ref;
use crate::undo::{UndoCommand, task_snapshot};

use super::TuiStore;

impl TuiStore {
    pub(crate) fn spawn_task_intake(
        &self,
        config: TaskIntakeConfig,
        input: String,
        project: Option<String>,
    ) -> JoinHandle<Result<TaskDraft>> {
        self.activate_workspace();
        let pool = self.pool.clone();
        let workspace = self.active_workspace.clone();
        tokio::spawn(async move {
            crate::workspaces::set_active_workspace(workspace);
            let context = {
                let mut conn = pool.acquire().await?;
                crate::task_intake::TaskIntakeContext::load_with_project(
                    &mut conn,
                    project.as_deref(),
                )
                .await?
            };
            let output =
                crate::task_intake::run_task_intake_command(&config, &context, &input).await?;
            let mut conn = pool.acquire().await?;
            crate::task_intake::parsed_output_to_draft(&mut conn, &context, &output).await
        })
    }

    pub(crate) async fn create_task(
        &mut self,
        draft: TaskDraft,
        current_selected_index: Option<usize>,
    ) -> Result<(String, Option<usize>)> {
        let previous_id = self
            .selected_task(current_selected_index)
            .map(|item| item.task.id.clone());
        self.activate_workspace();
        let mut conn = self.pool.acquire().await?;
        let outcome = create_task_operation(&mut conn, draft).await?;
        let task_id = outcome.task.id.clone();
        let message_ref = display_ref(&mut conn, &outcome.task).await?;
        let workspace_id = self.active_workspace.id.clone();
        let snapshot = task_snapshot(&mut conn, &workspace_id, &task_id).await?;
        drop(conn);
        self.record_undo_commands(
            &format!("task {task_id}"),
            vec![UndoCommand::DeleteCreatedTask {
                task_id: task_id.clone(),
                create_change_id: outcome.create_change_id,
                expected: snapshot,
            }],
        )
        .await?;

        self.refresh(None).await?;
        let created_index = self.tasks.iter().position(|item| item.task.id == task_id);
        if created_index.is_some() {
            return Ok((format!("created task {message_ref}"), created_index));
        }

        let restored = self.restored_task_selection(previous_id.as_deref());
        Ok((
            format!("created task {message_ref} hidden by current filters"),
            restored,
        ))
    }

    pub(crate) async fn add_note_to_task(&mut self, task_id: &str, body: String) -> Result<String> {
        self.activate_workspace();
        let workspace_id = self.active_workspace.id.clone();
        let mut conn = self.pool.acquire().await?;
        let outcome = add_note_operation(&mut conn, task_id, body).await?;
        let note_change_id: String = sqlx::query_scalar(
            "SELECT change_id FROM notes WHERE workspace_id = ? AND id = ? AND task_id = ?",
        )
        .bind(&workspace_id)
        .bind(&outcome.note_id)
        .bind(task_id)
        .fetch_one(&mut *conn)
        .await?;
        drop(conn);
        self.record_undo_commands(
            &format!("note {}", outcome.note_id),
            vec![UndoCommand::DeleteCreatedNote {
                task_id: task_id.to_string(),
                note_id: outcome.note_id.clone(),
                note_add_change_id: note_change_id,
            }],
        )
        .await?;
        Ok(outcome.note_id)
    }
}