From: Simon Tatham Date: Thu, 4 Jan 2024 08:06:15 +0000 (+0000) Subject: Implement viewing the thread of a selected post. X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=e2cce983ac8d5a75332c2ee067bf245a2ae25cee;p=mastodonochrome.git Implement viewing the thread of a selected post. Just as in the Python version, I provide an extra convenience method which calls the API service twice, once to find the post's ultimate top-level ancestor, and then again on that post to show the full thread (maximal connected component) that it's a part of, not just its own particular line. --- diff --git a/src/activity_stack.rs b/src/activity_stack.rs index acbf8cd..428565e 100644 --- a/src/activity_stack.rs +++ b/src/activity_stack.rs @@ -23,6 +23,7 @@ pub enum UtilityActivity { InfoStatus(String), ListStatusFavouriters(String), ListStatusBoosters(String), + ThreadFile(String, bool), } #[derive(PartialEq, Eq, Debug, Clone)] diff --git a/src/client.rs b/src/client.rs index 7cdf0a3..d938c08 100644 --- a/src/client.rs +++ b/src/client.rs @@ -870,4 +870,29 @@ impl Client { let verb = if enable {"reblog"} else {"unreblog"}; self.fave_boost_post(id, verb) } + + pub fn status_context(&mut self, id: &str) -> Result + { + let (url, req) = self.api_request(Req::get( + &format!("v1/statuses/{id}/context")))?; + let rsp = req.send()?; + let rspstatus = rsp.status(); + let ctx: Context = if !rspstatus.is_success() { + Err(ClientError::UrlError(url.clone(), rspstatus.to_string())) + } else { + match serde_json::from_str(&rsp.text()?) { + Ok(st) => Ok(st), + Err(e) => { + Err(ClientError::UrlError(url.clone(), e.to_string())) + } + } + }?; + for st in &ctx.ancestors { + self.cache_status(st); + } + for st in &ctx.descendants { + self.cache_status(st); + } + Ok(ctx) + } } diff --git a/src/file.rs b/src/file.rs index 907b3f4..39c8ea6 100644 --- a/src/file.rs +++ b/src/file.rs @@ -196,6 +196,7 @@ enum SelectionPurpose { StatusInfo, Favourite, Boost, + Thread, } #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -690,6 +691,8 @@ impl File { Err(_) => LogicalAction::Beep, } } + SelectionPurpose::Thread => LogicalAction::Goto( + UtilityActivity::ThreadFile(id, alt).into()), } } else { LogicalAction::Beep @@ -836,6 +839,10 @@ impl fs.add(Space, "Boost", 98) } } + SelectionPurpose::Thread => { + fs.add(Space, "Thread Context", 98) + .add(Pr('F'), "Full Thread", 97) + } }; fs.add(Pr('+'), "Down", 99) .add(Pr('-'), "Up", 99) @@ -942,6 +949,16 @@ impl } } + Pr('t') | Pr('T') => { + if Type::Item::can_highlight(HighlightType::Status) { + self.start_selection(HighlightType::Status, + SelectionPurpose::Thread, + client) + } else { + LogicalAction::Nothing + } + } + _ => LogicalAction::Nothing, } UIMode::Select(_, purpose) => match key { @@ -951,6 +968,11 @@ impl self.complete_selection(client, true), _ => LogicalAction::Nothing, } + Pr('f') | Pr('F') => match purpose { + SelectionPurpose::Thread => + self.complete_selection(client, true), + _ => LogicalAction::Nothing, + } Pr('-') | Up => self.selection_up(client), Pr('+') | Down => self.selection_down(client), Pr('q') | Pr('Q') => self.abort_selection(), @@ -1082,3 +1104,39 @@ pub fn view_single_post(client: &mut Client, status_id: &str) -> client, StaticSource::singleton(st.id), title)?; Ok(Box::new(file)) } + +pub fn view_thread(client: &mut Client, start_id: &str, full: bool) -> + Result, ClientError> +{ + let mut make_vec = |id: &str| -> Result, ClientError> { + let ctx = client.status_context(id)?; + let mut v = Vec::new(); + for st in &ctx.ancestors { + v.push(st.id.to_owned()); + } + v.push(id.to_owned()); + for st in &ctx.descendants { + v.push(st.id.to_owned()); + } + Ok(v) + }; + + let ids = make_vec(start_id)?; + let is_full = ids[0] == start_id; + let (ids, is_full) = if full && !is_full { + (make_vec(&ids[0])?, true) + } else { + (ids, is_full) + }; + + let title = if is_full { + format!("Full thread of post {}", start_id) + } else { + format!("Thread context of post {}", start_id) + }; + let title = ColouredString::uniform(&title, 'H'); + + let file = File::::new( + client, StaticSource::vector(ids), title)?; + Ok(Box::new(file)) +} diff --git a/src/tui.rs b/src/tui.rs index 35bebb3..50cd14d 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -461,6 +461,8 @@ fn new_activity_state(activity: Activity, client: &mut Client, compose_toplevel_post(client, post.unwrap_or_else(|| Post::new())), Activity::NonUtil(NonUtilityActivity::PostComposeMenu) => Ok(post_menu(post.expect("how did we get here without a Post?"))), + Activity::Util(UtilityActivity::ThreadFile(ref id, full)) => + view_thread(client, id, full), _ => todo!(), }; diff --git a/src/types.rs b/src/types.rs index 0449611..9597aeb 100644 --- a/src/types.rs +++ b/src/types.rs @@ -267,3 +267,9 @@ pub struct Instance { // FIXME: lots of things are missing from here! } + +#[derive(Deserialize, Debug, Clone)] +pub struct Context { + pub ancestors: Vec, + pub descendants: Vec, +}