bulletty 0.2.2

a pretty TUI feed reader (RSS+ATOM) that stores articles locally as Markdown files
use std::{cell::RefCell, rc::Rc};

use color_eyre::eyre::Result;
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use ratatui::layout::{Alignment, Constraint, Layout, Margin, Rect};
use ratatui::style::{Color, Style};
use ratatui::widgets::{Paragraph, Wrap};

use crate::app::AppWorkStatus;
use crate::core::library::feedlibrary::FeedLibrary;
use crate::core::ui::appscreen::{AppScreen, AppScreenEvent};
use crate::core::ui::dialog::Dialog;

pub struct WelcomeDialog {
    library: Rc<RefCell<FeedLibrary>>,
}

impl WelcomeDialog {
    pub fn new(library: Rc<RefCell<FeedLibrary>>) -> Self {
        Self { library }
    }
}

impl Dialog for WelcomeDialog {
    fn get_size(&self) -> Rect {
        Rect::new(70, 18, 0, 0)
    }

    fn as_screen(&self) -> &dyn AppScreen {
        self
    }

    fn as_screen_mut(&mut self) -> &mut dyn AppScreen {
        self
    }
}

impl AppScreen for WelcomeDialog {
    fn start(&mut self) {}

    fn quit(&mut self) {}

    fn pause(&mut self) {}

    fn unpause(&mut self) {}

    fn render(&mut self, frame: &mut ratatui::Frame, area: Rect) {
        let theme = {
            let library = self.library.borrow();
            library.settings.get_theme().unwrap().clone()
        };

        let layout = Layout::vertical([
            Constraint::Length(2),
            Constraint::Length(3),
            Constraint::Fill(1),
            Constraint::Length(1),
            Constraint::Length(2),
        ])
        .split(area.inner(Margin::new(3, 1)));

        let title = Paragraph::new("Welcome to bulletty!")
            .style(Style::new().fg(Color::from_u32(theme.base[0xa])))
            .alignment(Alignment::Center);

        let subtitle = Paragraph::new("Your library is empty. Add your first feed to get started.")
            .style(Style::new().fg(Color::from_u32(theme.base[0x5])))
            .alignment(Alignment::Center)
            .wrap(Wrap { trim: true });

        let instructions_text = concat!(
            "To add a feed, run the following command from your terminal:\n\n",
            "  bulletty add <feed_url> [category]\n\n",
            "Example:\n\n",
            "  bulletty add https://crocidb.com/index.xml Programming\n\n",
            "The category is optional. If omitted, the feed will be placed\n",
            "in the default category: General."
        );

        let instructions = Paragraph::new(instructions_text)
            .style(Style::new().fg(Color::from_u32(theme.base[0x6])))
            .alignment(Alignment::Left)
            .wrap(Wrap { trim: true });

        let hint = Paragraph::new("Press Esc or q to dismiss")
            .style(Style::new().fg(Color::from_u32(theme.base[0x3])))
            .alignment(Alignment::Center);

        frame.render_widget(title, layout[0]);
        frame.render_widget(subtitle, layout[1]);
        frame.render_widget(instructions, layout[2]);
        frame.render_widget(hint, layout[4]);
    }

    fn handle_event(&mut self, event: Event) -> Result<AppScreenEvent> {
        match event {
            Event::Key(key) if key.kind == KeyEventKind::Press => self.handle_keypress(key),
            Event::Mouse(_) => Ok(AppScreenEvent::None),
            Event::Resize(_, _) => Ok(AppScreenEvent::None),
            _ => Ok(AppScreenEvent::None),
        }
    }

    fn handle_keypress(&mut self, key: KeyEvent) -> Result<AppScreenEvent> {
        match (key.modifiers, key.code) {
            (_, KeyCode::Esc | KeyCode::Char('q') | KeyCode::Enter)
            | (KeyModifiers::CONTROL, KeyCode::Char('c') | KeyCode::Char('C')) => {
                Ok(AppScreenEvent::CloseDialog)
            }
            _ => Ok(AppScreenEvent::None),
        }
    }

    fn get_work_status(&self) -> AppWorkStatus {
        AppWorkStatus::None
    }

    fn get_title(&self) -> String {
        String::from("Welcome")
    }

    fn get_instructions(&self) -> String {
        String::from("Esc/q/Enter: dismiss")
    }

    fn get_full_instructions(&self) -> String {
        self.get_instructions()
    }
}