sara-tasks 2.2.0

Sara — folder-aware task manager
use std::time::{Duration as StdDuration, Instant};

use anyhow::Result;
use chrono::{Duration, Utc};
use rusqlite::Connection;

use crate::infrastructure::tui::theme::Theme;
use crate::infrastructure::{db, tui};

pub mod render;
pub mod state;

use state::{App, Mode, Outcome, is_minimal};

const POLL_MS: u64 = 500;
const TICK: StdDuration = StdDuration::from_secs(5);

pub struct FollowOptions {
    pub id: Option<String>,
    pub minimal: bool,
    pub project: Option<String>,
    pub all: bool,
    pub stall_mins: Option<i64>,
}

pub fn run(conn: &Connection, opts: FollowOptions) -> Result<()> {
    let mode = match &opts.id {
        Some(id) => Mode::Task(db::resolve_task(conn, id)?.uuid),
        None => Mode::Mission,
    };
    let stall = Duration::minutes(opts.stall_mins.unwrap_or(state::DEFAULT_STALL_MINS).max(1));
    let mut app = App::new(mode, opts.minimal, opts.project, opts.all, stall);
    app.refresh(conn, Utc::now())?;
    let theme = Theme::detect();

    tui::with_terminal(|terminal| {
        let mut version = db::data_version(conn)?;
        let mut last_refresh = Instant::now();
        let mut minimal = app.minimal;
        loop {
            terminal.draw(|f| {
                let a = f.area();
                minimal = is_minimal(app.minimal, a.width, a.height);
                render::render(f, &app, &theme);
            })?;
            if let Some(key) = tui::next_key(POLL_MS)? {
                let before = app.mode;
                if app.handle_key(key, minimal) == Outcome::Quit {
                    return Ok(());
                }
                if app.mode != before {
                    app.refresh(conn, Utc::now())?;
                    last_refresh = Instant::now();
                }
            }
            let now_version = db::data_version(conn)?;
            if now_version != version || last_refresh.elapsed() >= TICK {
                version = now_version;
                app.refresh(conn, Utc::now())?;
                last_refresh = Instant::now();
            }
        }
    })
}

#[cfg(test)]
#[path = "../../../tests/unit/commands/follow/tests.rs"]
mod tests;