temi 0.1.2

Command-line, lightweight TUI client for Lemmy
Documentation
use std::io;
use std::sync::{
    atomic::{AtomicBool, Ordering},
    Arc,
};

use crossterm::{
    event::{DisableMouseCapture, EnableMouseCapture},
    execute,
    terminal::{self, EnterAlternateScreen, LeaveAlternateScreen},
};
use tui::{backend::CrosstermBackend, Terminal};

use temi::{app::*, comments::*, endpoint::*, posts::*, screen::*, Result};

#[tokio::main]
async fn main() -> Result<()> {
    terminal::enable_raw_mode()?;

    let stop = Arc::new(AtomicBool::new(false));

    signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&stop))?;
    signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(&stop))?;

    let mut stdout = io::stdout();
    execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;

    let backend = CrosstermBackend::new(stdout);
    let mut terminal = Terminal::new(backend)?;

    let instance_url = std::env::var("LEMMY_INSTANCE").unwrap_or("https://voyager.lemmy.ml".into());

    let post_ep = Endpoint::PostList;
    let comment_ep = Endpoint::CommentList;

    let posts_res = dl_posts(format!("{instance_url}{post_ep}?page=1").as_str()).await?;
    let posts = PostResponseTable::from(posts_res);

    let mut app = App::new(instance_url, posts);

    loop {
        if stop.load(Ordering::Relaxed) {
            break;
        }

        match current_screen() {
            Screen::Post => {
                if let Some(post) = app.posts.current() {
                    let post_id = post.post.id();
                    let num_comments = post.counts.comments() as usize;

                    if app.comments.get(&post_id).is_none() || refresh() {
                        let instance_url = app.instance_url.as_str();
                        let mut responses = CommentResponses::new(Vec::with_capacity(num_comments));

                        for page in 0..(num_comments / 50) {
                            let page = page + 1;
                            let comment_url = format!(
                                "{instance_url}{comment_ep}?post_id={post_id}&page={page}&limit=50"
                            );
                            responses
                                .comments
                                .append(&mut dl_comments(comment_url.as_str()).await?.comments);
                        }

                        if num_comments % 50 > 0 {
                            let page = (num_comments / 50) + 1;
                            let comment_url = format!(
                                "{instance_url}{comment_ep}?post_id={post_id}&page={page}&limit=50"
                            );
                            responses
                                .comments
                                .append(&mut dl_comments(comment_url.as_str()).await?.comments);
                        }

                        app.comments.remove(&post_id);
                        app.comments.insert(post_id, responses.into());

                        set_refresh(false);
                    }

                    draw_post_screen(&mut terminal, app.as_mut(), Arc::clone(&stop))?;
                } else {
                    set_current_screen(Screen::PostList);
                }
            }
            Screen::PostList => {
                if download_posts() {
                    let instance_url = app.instance_url.as_str();
                    let page = app.page();

                    app.posts = dl_posts(format!("{instance_url}{post_ep}?page={page}").as_str())
                        .await?
                        .into();

                    set_download_posts(false);
                }
                draw_posts_screen(&mut terminal, app.as_mut(), Arc::clone(&stop))?
            }
            _ => (),
        }
    }

    terminal::disable_raw_mode()?;
    execute!(
        terminal.backend_mut(),
        LeaveAlternateScreen,
        DisableMouseCapture
    )?;
    terminal.show_cursor()?;

    Ok(())
}