From: Simon Tatham Date: Sun, 31 Dec 2023 08:58:43 +0000 (+0000) Subject: Add the Read Mentions feed. X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=16ceb5bb8b843b529fdfe3efcd73692ebd56a4bc;p=mastodonochrome.git Add the Read Mentions feed. --- diff --git a/src/client.rs b/src/client.rs index b70341c..fbf743d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -18,6 +18,7 @@ pub enum FeedId { Public, Hashtag(String), User(String, Boosts, Replies), + Mentions, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -342,6 +343,9 @@ impl Client { .param("exclude_reblogs", *boosts == Boosts::Hide) .param("exclude_replies", *replies == Replies::Hide) }, + FeedId::Mentions => { + Req::get("notifications").param("types[]", "mention") + }, }; let req = match ext { @@ -362,18 +366,52 @@ impl Client { let (url, req) = self.api_request(req)?; let body = req.send()?.text()?; - let sts: Vec = match serde_json::from_str(&body) { - Ok(sts) => Ok(sts), - Err(e) => { - dbg!(&body); - Err(ClientError::UrlError(url.clone(), e.to_string())) + + // Decode the JSON response as a different kind of type + // depending on the feed. But in all cases we expect to end up + // with a list of ids. + let ids: VecDeque = match id { + FeedId::Home | FeedId::Local | FeedId::Public | + FeedId::Hashtag(..) | FeedId::User(..) => { + let sts: Vec = match serde_json::from_str(&body) { + Ok(sts) => Ok(sts), + Err(e) => { + dbg!(&body); + Err(ClientError::UrlError(url.clone(), e.to_string())) + }, + }?; + for st in &sts { + self.cache_status(st); + } + sts.iter().rev().map(|st| st.id.clone()).collect() }, - }?; - let any_new = !sts.is_empty(); - for st in &sts { - self.cache_status(st); - } - let ids = sts.iter().rev().map(|st| st.id.clone()).collect(); + FeedId::Mentions => { + let mut nots: Vec = match serde_json::from_str( + &body) { + Ok(nots) => Ok(nots), + Err(e) => { + dbg!(&body); + Err(ClientError::UrlError(url.clone(), e.to_string())) + }, + }?; + + // According to the protocol spec, all notifications + // of type Mention should have a status in them. We + // double-check that here, so that the rest of our + // code can safely .unwrap() or .expect() the status + // from notifications they get via this feed. + nots.retain(|not| { + not.ntype == NotificationType::Mention && + not.status.is_some() + }); + for not in ¬s { + self.cache_notification(not); + } + nots.iter().rev().map(|not| not.id.clone()).collect() + }, + }; + let any_new = !ids.is_empty(); + match ext { FeedExtend::Initial => { self.feeds.insert(id.clone(), Feed { @@ -471,8 +509,14 @@ impl Client { match (up.id, up.response) { (StreamId::User, StreamResponse::Line(_)) => { - self.fetch_feed(&FeedId::Home, FeedExtend::Future)?; - updates.insert(FeedId::Home); + // This stream interleaves updates to the home + // timeline with notifications, so it can cause an + // update in more than one thing we think of as a feed. + for id in &[FeedId::Home, FeedId::Mentions] { + if self.fetch_feed(id, FeedExtend::Future)? { + updates.insert(id.clone()); + } + } }, // FIXME: we probably _should_ handle EOF from the diff --git a/src/file.rs b/src/file.rs index cae8abf..8828ea2 100644 --- a/src/file.rs +++ b/src/file.rs @@ -34,6 +34,20 @@ impl FeedType for StatusFeedType { } } +struct NotificationStatusFeedType {} +impl FeedType for NotificationStatusFeedType { + type Item = StatusDisplay; + + fn get_from_client(id: &str, client: &mut Client) -> + Result + { + let not = client.notification_by_id(&id)?; + let st = ¬.status.expect( + "expected all notifications in this feed would have statuses"); + Ok(StatusDisplay::new(st.clone(), client)) + } +} + struct FeedFileContents { id: FeedId, header: FileHeader, @@ -472,3 +486,13 @@ pub fn public_timeline(client: &mut Client) -> "HHHHHHHHHHHHHHHHHHHKH"))?; Ok(Box::new(file)) } + +pub fn mentions(client: &mut Client) -> + Result, ClientError> +{ + let file = FeedFile::::new( + client, FeedId::Mentions, ColouredString::general( + "Mentions [ESC][R]", + "HHHHHHHHHHHHKKKHHKH"))?; + Ok(Box::new(file)) +} diff --git a/src/menu.rs b/src/menu.rs index edd434f..bcfb580 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -218,6 +218,9 @@ pub fn utils_menu() -> Box { "Utilities [ESC]", "HHHHHHHHHHHKKKH"), false); + menu.add_action(Pr('R'), "Read Mentions", LogicalAction::Goto( + UtilityActivity::ReadMentions.into())); + menu.add_blank_line(); menu.add_action(Pr('E'), "Examine User", LogicalAction::NYI); menu.add_action(Pr('Y'), "Examine Yourself", LogicalAction::NYI); menu.add_blank_line(); diff --git a/src/tui.rs b/src/tui.rs index 331daec..d0754c9 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -411,6 +411,8 @@ fn new_activity_state(activity: Activity, client: &mut Client) -> public_timeline(client), Activity::NonUtil(NonUtilityActivity::LocalTimelineFile) => local_timeline(client), + Activity::Util(UtilityActivity::ReadMentions) => + mentions(client), _ => panic!("FIXME"), };