From: Simon Tatham Date: Sat, 3 Feb 2024 11:32:41 +0000 (+0000) Subject: Implement responding to follow requests. X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=145e02524c031d70554305f715c9db21aeccc69e;p=mastodonochrome.git Implement responding to follow requests. If a user has requested to follow you, you get an extra menu item in the options menu for that user, where you can accept or reject the request, or leave it unresponded-to for another time. To make this optional menu item less painful to implement, I made the MenuKeypressLineGeneral trait also apply to Option for any inner T that also implements it. So I can do all the routine admin like ensure_widths without writing three annoying 'if let Some(...)'. (But that doesn't let me get_value() on an optional CyclingMenuLine - that would be silly! For retrieving the value, or cycling the line, you still need to check if it's actually there.) --- diff --git a/src/client.rs b/src/client.rs index 29a1816..e15328f 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1499,6 +1499,17 @@ impl Client { Ok(()) } + pub fn respond_to_follow_request( + &mut self, + id: &str, + accept: bool, + ) -> Result<(), ClientError> { + let verb = if accept { "authorize" } else { "reject" }; + let req = Req::post(&format!("api/v1/follow_requests/{id}/{verb}")); + self.api_request_ok(req)?; + Ok(()) + } + pub fn set_account_flag( &mut self, id: &str, diff --git a/src/options.rs b/src/options.rs index 58d2d44..084a58d 100644 --- a/src/options.rs +++ b/src/options.rs @@ -321,6 +321,13 @@ impl EditableMenuLineData for LanguageVector { } } +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum FollowRequestResponse { + Accept, + Reject, + Undecided, +} + struct OtherUserOptionsMenu { title: FileHeader, normal_status: FileStatusLineFinal, @@ -328,6 +335,7 @@ struct OtherUserOptionsMenu { cl_follow: CyclingMenuLine, cl_boosts: CyclingMenuLine, el_languages: EditableMenuLine, + cl_follow_request: Option>, cl_block: CyclingMenuLine, cl_mute: CyclingMenuLine, id: String, @@ -408,6 +416,32 @@ impl OtherUserOptionsMenu { ), ); + let cl_follow_request = if rel.requested_by { + Some(CyclingMenuLine::new( + Pr('R'), + ColouredString::plain( + "Approve user's request to follow you: ", + ), + &[ + ( + FollowRequestResponse::Undecided, + ColouredString::plain("no action"), + ), + ( + FollowRequestResponse::Accept, + ColouredString::uniform("accept", 'f'), + ), + ( + FollowRequestResponse::Reject, + ColouredString::uniform("reject", 'r'), + ), + ], + FollowRequestResponse::Undecided, + )) + } else { + None + }; + let cl_block = CyclingMenuLine::new( Ctrl('B'), ColouredString::plain("Block this user: "), @@ -435,6 +469,7 @@ impl OtherUserOptionsMenu { cl_follow, cl_boosts, el_languages, + cl_follow_request, cl_block, cl_mute, id: id.to_owned(), @@ -452,18 +487,22 @@ impl OtherUserOptionsMenu { self.cl_follow.check_widths(&mut lmaxwid, &mut rmaxwid); self.cl_boosts.check_widths(&mut lmaxwid, &mut rmaxwid); self.el_languages.check_widths(&mut lmaxwid, &mut rmaxwid); + self.cl_follow_request + .check_widths(&mut lmaxwid, &mut rmaxwid); self.cl_block.check_widths(&mut lmaxwid, &mut rmaxwid); self.cl_mute.check_widths(&mut lmaxwid, &mut rmaxwid); self.cl_follow.reset_widths(); self.cl_boosts.reset_widths(); self.el_languages.reset_widths(); + self.cl_follow_request.reset_widths(); self.cl_block.reset_widths(); self.cl_mute.reset_widths(); self.cl_follow.ensure_widths(lmaxwid, rmaxwid); self.cl_boosts.ensure_widths(lmaxwid, rmaxwid); self.el_languages.ensure_widths(lmaxwid, rmaxwid); + self.cl_follow_request.ensure_widths(lmaxwid, rmaxwid); self.cl_block.ensure_widths(lmaxwid, rmaxwid); self.cl_mute.ensure_widths(lmaxwid, rmaxwid); @@ -506,6 +545,22 @@ impl OtherUserOptionsMenu { } } + if let Some(ref cl) = self.cl_follow_request { + let action = cl.get_value(); + let result = match action { + FollowRequestResponse::Accept + | FollowRequestResponse::Reject => client + .respond_to_follow_request( + &self.id, + action == FollowRequestResponse::Accept, + ), + FollowRequestResponse::Undecided => Ok(()), + }; + if result.is_err() { + return LogicalAction::Beep; // FIXME: report the error! + } + } + LogicalAction::Pop } } @@ -524,6 +579,7 @@ impl ActivityState for OtherUserOptionsMenu { lines.extend_from_slice(&self.cl_boosts.render(w)); lines.push(self.el_languages.render(w, &mut cursorpos, lines.len())); lines.extend_from_slice(&BlankLine::render_static()); + lines.extend_from_slice(&self.cl_follow_request.render(w)); lines.extend_from_slice(&self.cl_block.render(w)); lines.extend_from_slice(&self.cl_mute.render(w)); @@ -559,6 +615,13 @@ impl ActivityState for OtherUserOptionsMenu { Pr('l') | Pr('L') => self.el_languages.start_editing(), Ctrl('B') => self.cl_block.cycle(), Ctrl('U') => self.cl_mute.cycle(), + Pr('r') | Pr('R') => { + if let Some(ref mut cl) = self.cl_follow_request { + cl.cycle() + } else { + LogicalAction::Nothing + } + } _ => LogicalAction::Nothing, } } diff --git a/src/text.rs b/src/text.rs index bf6824f..e3e67f3 100644 --- a/src/text.rs +++ b/src/text.rs @@ -2042,6 +2042,24 @@ pub trait MenuKeypressLineGeneral { fn ensure_widths(&mut self, lmaxwid: usize, rmaxwid: usize); } +impl MenuKeypressLineGeneral for Option { + fn check_widths(&self, lmaxwid: &mut usize, rmaxwid: &mut usize) { + if let Some(inner) = self { + inner.check_widths(lmaxwid, rmaxwid); + } + } + fn reset_widths(&mut self) { + if let Some(inner) = self { + inner.reset_widths(); + } + } + fn ensure_widths(&mut self, lmaxwid: usize, rmaxwid: usize) { + if let Some(inner) = self { + inner.ensure_widths(lmaxwid, rmaxwid); + } + } +} + impl MenuKeypressLine { pub fn new(key: OurKey, description: ColouredString) -> Self { // +2 for [] around the key name