InfoStatus(String),
ListStatusFavouriters(String),
ListStatusBoosters(String),
+ ComposeReply(String),
+ PostReplyMenu(String),
ThreadFile(String, bool),
}
self.auth.account_id.clone()
}
+ pub fn our_account_fq(&self) -> String {
+ self.fq(&self.auth.username)
+ }
+
fn api_request_cl(&self, client: &reqwest::blocking::Client, req: Req) ->
Result<(String, reqwest::blocking::RequestBuilder), ClientError>
{
.param("sensitive", true)
.param("spoiler_text", text),
};
+ let req = match &post.m.in_reply_to_id {
+ None => req,
+ Some(id) => req
+ .param("in_reply_to_id", id),
+ };
let (url, req) = self.api_request(req)?;
let rsp = req.send()?;
keystate: ComposerKeyState,
goal_column: Option<usize>,
header: FileHeader,
+ irt: Option<InReplyToLine>,
headersep: EditorHeaderSeparator,
post_metadata: PostMetadata,
}
impl Composer {
- fn new(conf: InstanceStatusConfig, header: FileHeader, post: Post) -> Self
+ fn new(conf: InstanceStatusConfig, header: FileHeader,
+ irt: Option<InReplyToLine>, post: Post) -> Self
{
let point = post.text.len();
Composer {
keystate: ComposerKeyState::Start,
goal_column: None,
header,
+ irt,
headersep: EditorHeaderSeparator::new(),
post_metadata: post.m,
}
if self.last_size != Some((w, h)) {
self.last_size = Some((w, h));
self.page_len = h.saturating_sub(
- self.header.render(w).len() + self.headersep.render(w).len()
+ self.header.render(w).len() +
+ match self.irt {
+ None => 0,
+ Some(ref irt) => irt.render(w).len(),
+ } + self.headersep.render(w).len()
);
self.post_update();
}
{
let mut lines = Vec::new();
lines.extend_from_slice(&self.header.render(w));
+ if let Some(irt) = &self.irt {
+ lines.extend_from_slice(&irt.render(w));
+ }
lines.extend_from_slice(&self.headersep.render(w));
let ytop = 0; // FIXME: vary this to keep cursor in view
}
}
-pub fn compose_toplevel_post(client: &mut Client, post: Post) ->
+pub fn compose_post(client: &mut Client, post: Post) ->
Result<Box<dyn ActivityState>, ClientError>
{
let inst = client.instance()?;
- let header = FileHeader::new(ColouredString::uniform(
- "Compose a post", 'H'));
- Ok(Box::new(Composer::new(inst.configuration.statuses, header, post)))
+ let title = match post.m.in_reply_to_id {
+ None => "Compose a post".to_owned(),
+ Some(ref id) => format!("Reply to post {id}"),
+ };
+ let header = FileHeader::new(ColouredString::uniform(&title, 'H'));
+ let irt = match post.m.in_reply_to_id {
+ None => None,
+ Some(ref id) => Some(InReplyToLine::from_id(id, client)),
+ };
+ Ok(Box::new(Composer::new(inst.configuration.statuses, header, irt, post)))
}
Favourite,
Boost,
Thread,
+ Reply,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
Err(_) => LogicalAction::Beep,
}
}
+ SelectionPurpose::Reply => LogicalAction::Goto(
+ UtilityActivity::ComposeReply(id).into()),
SelectionPurpose::Thread => LogicalAction::Goto(
UtilityActivity::ThreadFile(id, alt).into()),
}
fs.add(Space, "Examine", 98),
SelectionPurpose::StatusInfo =>
fs.add(Space, "Info", 98),
+ SelectionPurpose::Reply =>
+ fs.add(Space, "Reply", 98),
SelectionPurpose::Favourite => {
if self.select_aux == Some(true) {
fs.add(Pr('D'), "Unfave", 98)
}
}
+ Pr('s') | Pr('S') => {
+ if Type::Item::can_highlight(HighlightType::WholeStatus) {
+ self.start_selection(HighlightType::WholeStatus,
+ SelectionPurpose::Reply,
+ client)
+ } else {
+ LogicalAction::Nothing
+ }
+ }
+
Pr('t') | Pr('T') => {
if Type::Item::can_highlight(HighlightType::Status) {
self.start_selection(HighlightType::Status,
use itertools::Itertools;
use std::cmp::max;
+use std::iter::once;
use strum::IntoEnumIterator;
-use super::client::Client;
+use super::client::{Client, ClientError};
use super::coloured_string::ColouredString;
use super::tui::{
ActivityState, CursorPosition, LogicalAction, OurKey, OurKey::*,
},
}
}
+
+ pub fn reply_to(id: &str, client: &mut Client) ->
+ Result<Self, ClientError>
+ {
+ let st = client.status_by_id(id)?.strip_boost();
+
+ let ourself = client.our_account_fq();
+
+ let userids = once(client.fq(&st.account.acct))
+ .chain(st.mentions.iter().map(|m| client.fq(&m.acct) ))
+ .filter(|acct| acct != &ourself);
+
+ let text = userids.map(|acct| format!("@{} ", acct)).collect();
+
+ Ok(Post {
+ text,
+ m: PostMetadata {
+ in_reply_to_id: Some(id.to_owned()),
+ visibility: st.visibility, // match the existing vis
+ content_warning: None,
+ language: "en".to_owned(), // FIXME: better default
+ },
+ })
+ }
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
Activity::Util(UtilityActivity::InfoStatus(ref id)) =>
view_single_post(client, id),
Activity::NonUtil(NonUtilityActivity::ComposeToplevel) =>
- compose_toplevel_post(client, post.unwrap_or_else(|| Post::new())),
+ compose_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::ComposeReply(ref id)) => {
+ let post = match post {
+ Some(post) => Ok(post),
+ None => Post::reply_to(id, client),
+ };
+ match post {
+ Ok(post) => compose_post(client, post),
+ Err(e) => Err(e),
+ }
+ }
+ Activity::Util(UtilityActivity::PostReplyMenu(_)) =>
+ 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!(),
}
LogicalAction::Error(_) => PhysicalAction::Beep, // FIXME: Error Log
LogicalAction::PostComposed(post) => {
- self.activity_stack.chain_to(
- NonUtilityActivity::PostComposeMenu.into());
+ let newact = match self.activity_stack.top() {
+ Activity::NonUtil(NonUtilityActivity::ComposeToplevel) =>
+ NonUtilityActivity::PostComposeMenu.into(),
+ Activity::Util(UtilityActivity::ComposeReply(id)) =>
+ UtilityActivity::PostReplyMenu(id.clone()).into(),
+ act => panic!("can't postcompose {act:?}"),
+ };
+ self.activity_stack.chain_to(newact);
self.changed_activity(client, Some(post));
PhysicalAction::Nothing
}
LogicalAction::PostReEdit(post) => {
- // FIXME: maybe not ComposeToplevel if we're replying
- self.activity_stack.chain_to(
- NonUtilityActivity::ComposeToplevel.into());
+ let newact = match self.activity_stack.top() {
+ Activity::NonUtil(NonUtilityActivity::PostComposeMenu) =>
+ NonUtilityActivity::ComposeToplevel.into(),
+ Activity::Util(UtilityActivity::PostReplyMenu(id)) =>
+ UtilityActivity::ComposeReply(id.clone()).into(),
+ act => panic!("can't reedit {act:?}"),
+ };
+ self.activity_stack.chain_to(newact);
self.changed_activity(client, Some(post));
PhysicalAction::Nothing
}
// pub filtered: Option<Vec<FilterResult>>,
}
+impl Status {
+ pub fn strip_boost(self) -> Status {
+ match self.reblog {
+ Some(b) => *b,
+ None => self,
+ }
+ }
+}
+
#[derive(Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
pub enum NotificationType {
#[serde(rename = "mention")] Mention,