tuisky 0.2.2

TUI client for Bluesky
Documentation
use bsky_sdk::api::app::bsky::actor::defs::{ProfileView, ProfileViewBasic};
use bsky_sdk::api::app::bsky::feed::defs::PostView;
use ratatui::style::{Style, Stylize};
use ratatui::text::Span;

pub trait Profile {
    fn display_name(&self) -> Option<&str>;
    fn handle(&self) -> &str;
}

impl Profile for ProfileView {
    fn display_name(&self) -> Option<&str> {
        self.display_name.as_deref().filter(|s| !s.is_empty())
    }
    fn handle(&self) -> &str {
        self.handle.as_str()
    }
}

impl Profile for ProfileViewBasic {
    fn display_name(&self) -> Option<&str> {
        self.display_name.as_deref().filter(|s| !s.is_empty())
    }
    fn handle(&self) -> &str {
        self.handle.as_str()
    }
}

pub fn profile_name_as_str(author: &dyn Profile) -> &str {
    author.display_name().unwrap_or(author.handle())
}

pub fn profile_name(author: &dyn Profile) -> Vec<Span<'_>> {
    if let Some(display_name) = author.display_name() {
        vec![
            Span::from(display_name.to_string()).bold(),
            Span::from(" "),
            format!("@{}", author.handle()).gray(),
        ]
    } else {
        vec![format!("@{}", author.handle()).bold()]
    }
}

pub fn counts(post_view: &PostView, pad: usize) -> Vec<Span<'_>> {
    let (mut reposted, mut liked) = (false, false);
    if let Some(viewer) = &post_view.viewer {
        reposted = viewer.repost.is_some();
        liked = viewer.like.is_some();
    }
    let (replies, reposts, quotes, likes) = (
        post_view.reply_count.unwrap_or_default(),
        post_view.repost_count.unwrap_or_default(),
        post_view.quote_count.unwrap_or_default(),
        post_view.like_count.unwrap_or_default(),
    );
    let style = |b| {
        if b {
            Style::default()
        } else {
            Style::default().dim()
        }
    };
    vec![
        Span::from(format!("{replies:pad$} replies")).style(style(replies > 0)),
        Span::from(", ").dim(),
        Span::from(format!("{reposts:pad$}")).style(if reposted {
            Style::default().green()
        } else {
            style(reposts > 0)
        }),
        Span::from(" reposts").style(style(reposts > 0)),
        Span::from(", ").dim(),
        Span::from(format!("{quotes:pad$}")).style(style(reposts > 0)),
        Span::from(" quotes").style(style(reposts > 0)),
        Span::from(", ").dim(),
        Span::from(format!("{likes:pad$}")).style(if liked {
            Style::default().red()
        } else {
            style(likes > 0)
        }),
        Span::from(" likes").style(style(likes > 0)),
    ]
}