}
}
+impl std::fmt::Display for ClientError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) ->
+ Result<(), std::fmt::Error>
+ {
+ match self {
+ ClientError::InternalError(ref msg) =>
+ write!(f, "internal failure: {}", msg),
+ ClientError::UrlParseError(ref url, ref msg) =>
+ write!(f, "Parse failure {} (retrieving URL: {})", msg, url),
+ ClientError::UrlError(ref url, ref msg) =>
+ write!(f, "{} (retrieving URL: {})", msg, url),
+ }
+ }
+}
+
// Our own struct to collect the pieces of an HTTP request before we
// pass it on to reqwests. Allows incremental adding of request parameters.
struct Req {
self.permit_write = permit;
}
+ pub fn fq(&self, acct: &str) -> String {
+ match acct.contains('@') {
+ true => acct.to_owned(),
+ false => acct.to_owned() + "@" + &self.auth.instance_domain,
+ }
+ }
+
fn api_request(&self, req: Req) ->
Result<(String, reqwest::blocking::RequestBuilder), ClientError>
{
use unicode_width::UnicodeWidthStr;
use super::html;
+use super::client::Client;
use super::types::*;
use super::tui::OurKey;
use super::coloured_string::{ColouredString, ColouredStringSlice};
}
impl Media {
- pub fn new(url: &str, description: &str)
+ pub fn new(url: &str, description: Option<&str>)
-> Self {
- let mut paras = description
- .split('\n')
- .map(|x| Paragraph::new()
- .set_indent(2, 2)
- .add(&ColouredString::uniform(x, 'm')))
- .collect();
- trim_para_list(&mut paras);
+ let paras = match description {
+ None => Vec::new(),
+ Some(description) => {
+ let mut paras = description
+ .split('\n')
+ .map(|x| Paragraph::new()
+ .set_indent(2, 2)
+ .add(&ColouredString::uniform(x, 'm')))
+ .collect();
+ trim_para_list(&mut paras);
+ paras
+ },
+ };
+
Media {
url: url.to_owned(),
description: paras,
#[test]
fn test_media() {
assert_eq!(Media::new("https://example.com/example.png",
- "A picture of an example, sitting on an example, with examples dotted around the example landscape.\n\nThis doesn't really make sense, but whatever.").render(50), vec! {
+ Some("A picture of an example, sitting on an example, with examples dotted around the example landscape.\n\nThis doesn't really make sense, but whatever.")).render(50), vec! {
ColouredString::uniform("https://example.com/example.png", 'M'),
ColouredString::general(" A picture of an example, sitting on an example,",
" mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm"),
}
pub struct StatusDisplay {
- st: Status,
- via: Option<Account>,
+ sep: SeparatorLine,
+ from: UsernameHeader,
+ via: Option<UsernameHeader>,
+ irt: Option<InReplyToLine>,
+ content: Vec<Paragraph>,
+ media: Vec<Media>,
+ blank: BlankLine,
}
impl StatusDisplay {
- pub fn new(st: Status) -> Self {
+ pub fn new(st: Status, client: &mut Client) -> Self {
let (st, via) = match st.reblog {
Some(b) => (*b, Some(st.account)),
None => (st, None),
};
+ let sep = SeparatorLine::new(
+ Some(st.created_at),
+ st.favourited == Some(true),
+ st.reblogged == Some(true));
+
+ let from = UsernameHeader::from(
+ &client.fq(&st.account.acct), &st.account.display_name);
+
+ let via = match via {
+ None => None,
+ Some(booster) => Some(UsernameHeader::via(
+ &client.fq(&booster.acct), &booster.display_name)),
+ };
+
+ let irt = match &st.in_reply_to_id {
+ None => None,
+ Some(id) => {
+ let parent_text = match client.status_by_id(id) {
+ Ok(st) => parse_html(&st.content),
+ Err(e) => {
+ vec! { Paragraph::new().add(&ColouredString::plain(
+ &format!("[unavailable: {}]", e)
+ )) }
+ },
+ };
+ Some(InReplyToLine::new(&parent_text))
+ },
+ };
+
+ let content = parse_html(&st.content);
+
+ let media = st.media_attachments.iter().map(|m| {
+ let desc_ref = match &m.description {
+ Some(s) => Some(&s as &str),
+ None => None,
+ };
+ Media::new(&m.url, desc_ref)
+ }).collect();
+
StatusDisplay {
- st: st,
+ sep: sep,
+ from: from,
via: via,
+ irt: irt,
+ content: content,
+ media: media,
+ blank: BlankLine::new(),
}
}
}
+fn push_fragment(lines: &mut Vec<ColouredString>, frag: Vec<ColouredString>) {
+ lines.extend(frag.iter().map(|line| line.to_owned()));
+}
+
impl TextFragment for StatusDisplay {
fn render(&self, width: usize) -> Vec<ColouredString> {
let mut lines = Vec::new();
- let sep = SeparatorLine::new(
- Some(self.st.created_at),
- self.st.favourited == Some(true),
- self.st.reblogged == Some(true));
- lines.extend(sep.render(width).iter().map(|line| line.to_owned()));
+ push_fragment(&mut lines, self.sep.render(width));
+ push_fragment(&mut lines, self.from.render(width));
+ if let Some(via) = &self.via {
+ push_fragment(&mut lines, via.render(width));
+ }
+ if let Some(irt) = &self.irt {
+ push_fragment(&mut lines,irt.render(width));
+ }
+ push_fragment(&mut lines, self.blank.render(width));
+ for para in &self.content {
+ push_fragment(&mut lines, para.render(width));
+ }
+ if self.content.len() > 0 {
+ push_fragment(&mut lines, self.blank.render(width));
+ }
+ for m in &self.media {
+ push_fragment(&mut lines, m.render(width));
+ push_fragment(&mut lines, self.blank.render(width));
+ }
lines
}