chiark / gitweb /
Implement viewing the thread of a selected post.
authorSimon Tatham <anakin@pobox.com>
Thu, 4 Jan 2024 08:06:15 +0000 (08:06 +0000)
committerSimon Tatham <anakin@pobox.com>
Thu, 4 Jan 2024 08:16:16 +0000 (08:16 +0000)
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.

src/activity_stack.rs
src/client.rs
src/file.rs
src/tui.rs
src/types.rs

index acbf8cdd9182610e15e8ffe9549ac127835f7eb0..428565ebb824aec8daada3df027dcf2da1210042 100644 (file)
@@ -23,6 +23,7 @@ pub enum UtilityActivity {
     InfoStatus(String),
     ListStatusFavouriters(String),
     ListStatusBoosters(String),
+    ThreadFile(String, bool),
 }
 
 #[derive(PartialEq, Eq, Debug, Clone)]
index 7cdf0a3be61a58cb1317023d9bdf10fdaa1724f6..d938c0827c15b2db2929703983e0129bed8b7d8b 100644 (file)
@@ -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<Context, ClientError>
+    {
+        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)
+    }
 }
index 907b3f453004a8f6cfc6be76ae731f3e8c6dbfa2..39c8ea63a80d05d6b8364ef42ab999505ad84c00 100644 (file)
@@ -196,6 +196,7 @@ enum SelectionPurpose {
     StatusInfo,
     Favourite,
     Boost,
+    Thread,
 }
 
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
@@ -690,6 +691,8 @@ impl<Type: FileType, Source: FileDataSource> File<Type, Source> {
                         Err(_) => LogicalAction::Beep,
                     }
                 }
+                SelectionPurpose::Thread => LogicalAction::Goto(
+                    UtilityActivity::ThreadFile(id, alt).into()),
             }
         } else {
             LogicalAction::Beep
@@ -836,6 +839,10 @@ impl<Type: FileType, Source: FileDataSource>
                             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<Type: FileType, Source: FileDataSource>
                     }
                 }
 
+                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<Type: FileType, Source: FileDataSource>
                         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<Box<dyn ActivityState>, ClientError>
+{
+    let mut make_vec = |id: &str| -> Result<Vec<String>, 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::<StatusFeedType, _>::new(
+        client, StaticSource::vector(ids), title)?;
+    Ok(Box::new(file))
+}
index 35bebb3e1a3fd704b8be0f205e72ba139fe6a8d7..50cd14deee20d9a0c332fbd69e2b8b7d6697a582 100644 (file)
@@ -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!(),
     };
 
index 044961165afcd341b37ecef5ed25a72834d110be..9597aeb4ecd2e5513e3bbb109a86d07ec6243fbe 100644 (file)
@@ -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<Status>,
+    pub descendants: Vec<Status>,
+}