From c7398a50a2f41f5eb5bc02fcf5bf9b5d20bd8d4b Mon Sep 17 00:00:00 2001 From: Mike Dilger Date: Wed, 4 Jan 2023 14:31:05 +1300 Subject: [PATCH] Replies feed --- src/feed.rs | 49 ++++++++++++++++++++++++++++++++------ src/overlord/minion/mod.rs | 2 +- src/ui/feed.rs | 22 ++++++++++++++--- src/ui/mod.rs | 4 +++- 4 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/feed.rs b/src/feed.rs index 6b0fe9a1..09cc9c64 100644 --- a/src/feed.rs +++ b/src/feed.rs @@ -3,11 +3,13 @@ use crate::globals::GLOBALS; use nostr_types::PublicKeyHex; use nostr_types::{Event, EventKind, Id}; use parking_lot::RwLock; +use std::collections::HashSet; use std::time::{Duration, Instant}; #[derive(Clone, Debug)] pub enum FeedKind { General, + Replies, Thread(Id), Person(PublicKeyHex), } @@ -16,6 +18,7 @@ pub struct Feed { current_feed_kind: RwLock, general_feed: RwLock>, + replies_feed: RwLock>, // We only recompute the feed at specified intervals interval_ms: RwLock, @@ -31,6 +34,7 @@ impl Feed { Feed { current_feed_kind: RwLock::new(FeedKind::General), general_feed: RwLock::new(Vec::new()), + replies_feed: RwLock::new(Vec::new()), interval_ms: RwLock::new(1000), // Every second, until we load from settings last_computed: RwLock::new(Instant::now()), my_event_ids: RwLock::new(Vec::new()), @@ -45,6 +49,10 @@ impl Feed { *self.current_feed_kind.write() = FeedKind::General; } + pub fn set_feed_to_replies(&self) { + *self.current_feed_kind.write() = FeedKind::Replies; + } + pub fn set_feed_to_thread(&self, id: Id) { let _ = GLOBALS.to_minions.send(BusMessage { target: "all".to_string(), @@ -78,6 +86,17 @@ impl Feed { self.general_feed.read().clone() } + pub fn get_replies(&self) -> Vec { + let now = Instant::now(); + if *self.last_computed.read() + Duration::from_millis(*self.interval_ms.read() as u64) < now + { + self.recompute(); + *self.last_computed.write() = now; + } + + self.replies_feed.read().clone() + } + pub fn get_thread_parent(&self, id: Id) -> Id { let mut event = match GLOBALS.events.blocking_read().get(&id).cloned() { None => return id, @@ -173,17 +192,33 @@ impl Feed { }) .collect(); - // Filter further for the feed - let mut events: Vec = events + // Filter further for the general feed + let mut fevents: Vec = events .iter() - .filter(|e| pubkeys.contains(&e.pubkey.into())) // something we follow .filter(|e| !GLOBALS.dismissed.blocking_read().contains(&e.id)) + .filter(|e| pubkeys.contains(&e.pubkey.into())) // something we follow .cloned() .collect(); + fevents.sort_by(|a, b| b.created_at.cmp(&a.created_at)); + *self.general_feed.write() = fevents.iter().map(|e| e.id).collect(); - // In time order - events.sort_by(|a, b| b.created_at.cmp(&a.created_at)); - - *self.general_feed.write() = events.iter().map(|e| e.id).collect(); + // Filter differently for the replies feed + let my_events: HashSet = self.my_event_ids.read().iter().copied().collect(); + let mut revents: Vec = events + .iter() + .filter(|e| !GLOBALS.dismissed.blocking_read().contains(&e.id)) + .filter(|e| { + // FIXME: maybe try replies_to_ancestors to go deeper + if let Some((id, _)) = e.replies_to() { + if my_events.contains(&id) { + return true; + } + } + false + }) + .cloned() + .collect(); + revents.sort_by(|a, b| b.created_at.cmp(&a.created_at)); + *self.replies_feed.write() = revents.iter().map(|e| e.id).collect(); } } diff --git a/src/overlord/minion/mod.rs b/src/overlord/minion/mod.rs index 74240005..aff411cf 100644 --- a/src/overlord/minion/mod.rs +++ b/src/overlord/minion/mod.rs @@ -264,7 +264,7 @@ impl Minion { // Any mentions of me filters.push(Filter { p: vec![pubkey.into()], - since: Some(Unixtime::now().unwrap() - Duration::from_secs(feed_chunk)), + since: Some(Unixtime::now().unwrap() - Duration::from_secs(feed_chunk * 10)), // further back ..Default::default() }); diff --git a/src/ui/feed.rs b/src/ui/feed.rs index 57dad07b..67e895b4 100644 --- a/src/ui/feed.rs +++ b/src/ui/feed.rs @@ -22,6 +22,7 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram let mut feed_kind = GLOBALS.feed.get_feed_kind(); app.page = match feed_kind { FeedKind::General => Page::FeedGeneral, + FeedKind::Replies => Page::FeedReplies, FeedKind::Thread(_) => Page::FeedThread, FeedKind::Person(_) => Page::FeedPerson, }; @@ -39,13 +40,24 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram feed_kind = FeedKind::General; } ui.separator(); + if ui + .add(SelectableLabel::new( + app.page == Page::FeedReplies, + "Replies", + )) + .clicked() + { + app.page = Page::FeedReplies; + GLOBALS.feed.set_feed_to_replies(); + feed_kind = FeedKind::Replies; + } if matches!(feed_kind, FeedKind::Thread(..)) { - ui.selectable_value(&mut app.page, Page::FeedThread, "Thread"); ui.separator(); + ui.selectable_value(&mut app.page, Page::FeedThread, "Thread"); } if matches!(feed_kind, FeedKind::Person(..)) { - ui.selectable_value(&mut app.page, Page::FeedPerson, "Person"); ui.separator(); + ui.selectable_value(&mut app.page, Page::FeedPerson, "Person"); } }); ui.separator(); @@ -103,7 +115,7 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram if ui.link("setup your identity").clicked() { app.page = Page::You; } - ui.label(" to post."); + ui.label(" to post or see your replies."); }); } else if !GLOBALS.relays.blocking_read().iter().any(|(_, r)| r.post) { ui.horizontal(|ui| { @@ -177,6 +189,10 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram let feed = GLOBALS.feed.get_general(); render_a_feed(app, ctx, frame, ui, feed, false); } + FeedKind::Replies => { + let feed = GLOBALS.feed.get_replies(); + render_a_feed(app, ctx, frame, ui, feed, true); + } FeedKind::Thread(id) => { let parent = GLOBALS.feed.get_thread_parent(id); render_a_feed(app, ctx, frame, ui, vec![parent], true); diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 922031db..e949199c 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -55,6 +55,7 @@ pub fn run() -> Result<(), Error> { #[derive(PartialEq)] enum Page { FeedGeneral, + FeedReplies, FeedThread, FeedPerson, PeopleList, @@ -189,6 +190,7 @@ impl eframe::App for GossipUi { if ui .add(SelectableLabel::new( self.page == Page::FeedGeneral + || self.page == Page::FeedReplies || self.page == Page::FeedThread || self.page == Page::FeedPerson, "Feed", @@ -261,7 +263,7 @@ impl eframe::App for GossipUi { }); egui::CentralPanel::default().show(ctx, |ui| match self.page { - Page::FeedGeneral | Page::FeedThread | Page::FeedPerson => { + Page::FeedGeneral | Page::FeedReplies | Page::FeedThread | Page::FeedPerson => { feed::update(self, ctx, frame, ui) } Page::PeopleList | Page::PeopleFollow | Page::Person => {