bulletty 0.1.1

a terminal feed (RSS+ATOM) reader
Documentation
use ratatui::{
    style::{Color, Style, Stylize},
    text::{Line, Span, Text},
    widgets::{ListItem, ListState},
};

use crate::{
    core::feed::feedentry::FeedEntry,
    core::library::feedlibrary::FeedLibrary,
    ui::states::feedtreestate::{FeedItemInfo, FeedTreeState},
};

pub struct FeedEntryState {
    pub entries: Vec<FeedEntry>,
    pub listatate: ListState,
    pub previous_selected: String,
}

impl Default for FeedEntryState {
    fn default() -> Self {
        Self::new()
    }
}

impl FeedEntryState {
    pub fn new() -> Self {
        Self {
            entries: vec![],
            listatate: ListState::default().with_selected(Some(0)),
            previous_selected: String::new(),
        }
    }

    pub fn update(&mut self, library: &FeedLibrary, treestate: &FeedTreeState) {
        let prev = self.previous_selected.to_string();

        self.entries = match treestate.get_selected() {
            Some(FeedItemInfo::Category(t)) => {
                self.previous_selected = t.to_string();
                library.get_feed_entries_by_category(t)
            }
            Some(FeedItemInfo::Item(_, _, s)) => {
                self.previous_selected = s.to_string();
                library.get_feed_entries_by_item_slug(s)
            }
            None => vec![],
        };

        if prev != self.previous_selected {
            self.listatate.select_first();
        }
    }

    pub fn get_items(&self) -> Vec<ListItem> {
        self.entries
            .iter()
            .map(|entry| {
                let mut item_content_lines: Vec<Line> = Vec::new();

                item_content_lines.push(Line::from(""));

                if !entry.seen {
                    item_content_lines.push(Line::from(Span::styled(
                        format!("\u{f1ea} {} \u{e3e3}", entry.title),
                        Style::default().bold().fg(Color::from_u32(0x81ae80)),
                    )));
                } else {
                    item_content_lines.push(Line::from(Span::styled(
                        format!("\u{f1ea} {}", entry.title),
                        Style::default().bold(),
                    )));
                };

                item_content_lines.push(Line::from(Span::styled(
                    format!(
                        "\u{f0520} {}",
                        entry.date.with_timezone(&chrono::Local).format("%Y-%m-%d")
                    ),
                    Style::default().italic().dim(),
                )));

                item_content_lines.push(Line::from(Span::styled(
                    format!("{}...", entry.description),
                    Style::default().dim(),
                )));

                item_content_lines.push(Line::from(""));

                let item_text = Text::from(item_content_lines);
                ListItem::new(item_text)
            })
            .collect()
    }

    pub fn get_selected(&self) -> Option<FeedEntry> {
        match self.listatate.selected() {
            None => None,
            Some(selected) => {
                if selected < self.entries.len() {
                    Some(self.entries[selected].clone())
                } else {
                    None
                }
            }
        }
    }

    pub fn set_current_read(&mut self) {
        if let Some(selected) = self.listatate.selected() {
            if selected < self.entries.len() {
                self.entries[selected].seen = true;
            }
        }
    }

    pub fn select_next(&mut self) {
        if self.entries.is_empty() {
            return;
        }

        if self.listatate.selected().unwrap_or(0) < self.entries.len() - 1 {
            self.listatate.select_next();
        }
    }

    pub fn select_previous(&mut self) {
        if self.entries.is_empty() {
            return;
        }

        if self.listatate.selected().unwrap_or(0) > 0 {
            self.listatate.select_previous();
        }
    }

    pub fn select_first(&mut self) {
        if self.entries.is_empty() {
            return;
        }

        self.listatate.select_first();
    }

    pub fn select_last(&mut self) {
        if self.entries.is_empty() {
            return;
        }

        self.listatate.select(Some(self.entries.len() - 1));
    }
}