Multiple feeds working

This commit is contained in:
Mike Dilger 2023-01-03 13:46:57 +13:00
parent 238c429017
commit 611b5ab12c
6 changed files with 287 additions and 222 deletions

View File

@ -1,10 +1,20 @@
use crate::globals::GLOBALS; use crate::globals::GLOBALS;
use nostr_types::PublicKeyHex;
use nostr_types::{Event, EventKind, Id}; use nostr_types::{Event, EventKind, Id};
use std::time::{Duration, Instant};
use parking_lot::RwLock; use parking_lot::RwLock;
use std::time::{Duration, Instant};
#[derive(Clone, Debug)]
pub enum FeedKind {
General,
Thread(Id),
Person(PublicKeyHex),
}
pub struct Feed { pub struct Feed {
feed: RwLock<Vec<Id>>, current_feed_kind: RwLock<FeedKind>,
general_feed: RwLock<Vec<Id>>,
// We only recompute the feed at specified intervals // We only recompute the feed at specified intervals
interval_ms: RwLock<u32>, interval_ms: RwLock<u32>,
@ -18,7 +28,8 @@ pub struct Feed {
impl Feed { impl Feed {
pub fn new() -> Feed { pub fn new() -> Feed {
Feed { Feed {
feed: RwLock::new(Vec::new()), current_feed_kind: RwLock::new(FeedKind::General),
general_feed: RwLock::new(Vec::new()),
interval_ms: RwLock::new(1000), // Every second, until we load from settings interval_ms: RwLock::new(1000), // Every second, until we load from settings
last_computed: RwLock::new(Instant::now()), last_computed: RwLock::new(Instant::now()),
my_event_ids: RwLock::new(Vec::new()), my_event_ids: RwLock::new(Vec::new()),
@ -26,14 +37,77 @@ impl Feed {
} }
} }
pub fn get(&self) -> Vec<Id> { pub fn set_feed_to_general(&self) {
*self.current_feed_kind.write() = FeedKind::General;
}
pub fn set_feed_to_thread(&self, id: Id) {
// get parent?
*self.current_feed_kind.write() = FeedKind::Thread(id);
}
pub fn set_feed_to_person(&self, pubkey: PublicKeyHex) {
// FIXME - TRIGGER OVERLORD TO FETCH THEIR EVENTS FURTHER BACK
*self.current_feed_kind.write() = FeedKind::Person(pubkey);
}
pub fn get_feed_kind(&self) -> FeedKind {
self.current_feed_kind.read().to_owned()
}
pub fn get_general(&self) -> Vec<Id> {
let now = Instant::now(); let now = Instant::now();
if *self.last_computed.read() + Duration::from_millis(*self.interval_ms.read() as u64) < now { if *self.last_computed.read() + Duration::from_millis(*self.interval_ms.read() as u64) < now
{
self.recompute(); self.recompute();
*self.last_computed.write() = now; *self.last_computed.write() = now;
} }
self.feed.read().clone() self.general_feed.read().clone()
}
pub fn get_thread_parent(&self, id: Id) -> Id {
// FIXME - TRIGGER OVERLORD TO FETCH THIS FEED
let mut event = match GLOBALS.events.blocking_read().get(&id).cloned() {
None => return id,
Some(e) => e,
};
// Try for root
if let Some((root, _)) = event.replies_to_root() {
if GLOBALS.events.blocking_read().contains_key(&root) {
return root;
}
}
// Climb parents as high as we can
while let Some((parent, _)) = event.replies_to() {
if let Some(e) = GLOBALS.events.blocking_read().get(&parent) {
event = e.to_owned();
} else {
break;
}
}
// The highest event id we have
event.id
}
pub fn get_person_feed(&self, person: PublicKeyHex) -> Vec<Id> {
let mut events: Vec<Event> = GLOBALS
.events
.blocking_read()
.iter()
.map(|(_, e)| e)
.filter(|e| e.kind == EventKind::TextNote)
.filter(|e| e.pubkey.as_hex_string() == person.0)
.filter(|e| !GLOBALS.dismissed.blocking_read().contains(&e.id))
.map(|e| e.to_owned())
.collect();
events.sort_unstable_by(|a, b| b.created_at.cmp(&a.created_at));
events.iter().map(|e| e.id).collect()
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -88,26 +162,11 @@ impl Feed {
let mut events: Vec<Event> = events let mut events: Vec<Event> = events
.iter() .iter()
.filter(|e| !GLOBALS.dismissed.blocking_read().contains(&e.id)) .filter(|e| !GLOBALS.dismissed.blocking_read().contains(&e.id))
//.filter(|e| { for Threaded
//e.replies_to().is_none()
//})
.cloned() .cloned()
.collect(); .collect();
/* for threaded
if settings.view_threaded {
events.sort_unstable_by(|a, b| {
let a_last = GLOBALS.last_reply.blocking_read().get(&a.id).cloned();
let b_last = GLOBALS.last_reply.blocking_read().get(&b.id).cloned();
let a_time = a_last.unwrap_or(a.created_at);
let b_time = b_last.unwrap_or(b.created_at);
b_time.cmp(&a_time)
});
}
*/
events.sort_unstable_by(|a, b| b.created_at.cmp(&a.created_at)); events.sort_unstable_by(|a, b| b.created_at.cmp(&a.created_at));
*self.feed.write() = events.iter().map(|e| e.id).collect(); *self.general_feed.write() = events.iter().map(|e| e.id).collect();
} }
} }

View File

@ -1,18 +1,16 @@
use super::{GossipUi, Page}; use super::{GossipUi, Page};
use crate::comms::BusMessage; use crate::comms::BusMessage;
use crate::feed::FeedKind;
use crate::globals::{Globals, GLOBALS}; use crate::globals::{Globals, GLOBALS};
use crate::ui::widgets::{CopyButton, ReplyButton}; use crate::ui::widgets::{CopyButton, ReplyButton};
use eframe::egui; use eframe::egui;
use egui::{ use egui::{
Align, Color32, Context, Frame, Image, Label, Layout, RichText, ScrollArea, Sense, TextEdit, Align, Color32, Context, Frame, Image, Layout, RichText, ScrollArea, SelectableLabel, Sense,
Ui, Vec2, TextEdit, Ui, Vec2,
}; };
use linkify::{LinkFinder, LinkKind}; use linkify::{LinkFinder, LinkKind};
use nostr_types::{EventKind, Id, PublicKeyHex}; use nostr_types::{EventKind, Id, PublicKeyHex};
mod person;
mod thread;
struct FeedPostParams { struct FeedPostParams {
id: Id, id: Id,
indent: usize, indent: usize,
@ -21,23 +19,37 @@ struct FeedPostParams {
} }
pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Frame, ui: &mut Ui) { pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Frame, ui: &mut Ui) {
let mut feed_kind = GLOBALS.feed.get_feed_kind();
app.page = match feed_kind {
FeedKind::General => Page::FeedGeneral,
FeedKind::Thread(_) => Page::FeedThread,
FeedKind::Person(_) => Page::FeedPerson,
};
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.selectable_value(&mut app.page, Page::FeedGeneral, "Following"); if ui
.add(SelectableLabel::new(
app.page == Page::FeedGeneral,
"Following",
))
.clicked()
{
app.page = Page::FeedGeneral;
GLOBALS.feed.set_feed_to_general();
feed_kind = FeedKind::General;
}
ui.separator(); ui.separator();
if app.feed_thread_id.is_some() { if matches!(feed_kind, FeedKind::Thread(..)) {
ui.selectable_value(&mut app.page, Page::FeedThread, "Thread"); ui.selectable_value(&mut app.page, Page::FeedThread, "Thread");
ui.separator(); ui.separator();
} }
if app.feed_person_pubkey.is_some() { if matches!(feed_kind, FeedKind::Person(..)) {
ui.selectable_value(&mut app.page, Page::FeedPerson, "Person"); ui.selectable_value(&mut app.page, Page::FeedPerson, "Person");
ui.separator(); ui.separator();
} }
}); });
ui.separator(); ui.separator();
if app.page == Page::FeedGeneral {
let feed = GLOBALS.feed.get();
Globals::trim_desired_events_sync(); Globals::trim_desired_events_sync();
let desired_count: isize = match GLOBALS.desired_events.try_read() { let desired_count: isize = match GLOBALS.desired_events.try_read() {
Ok(v) => v.len() as isize, Ok(v) => v.len() as isize,
@ -75,12 +87,6 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram
}); });
} }
if ui.button("close all").clicked() {
app.hides = feed.clone();
}
if ui.button("open all").clicked() {
app.hides.clear();
}
ui.label(&format!( ui.label(&format!(
"RIF={}", "RIF={}",
GLOBALS GLOBALS
@ -166,8 +172,30 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram
ui.separator(); ui.separator();
let threaded = false; match feed_kind {
FeedKind::General => {
let feed = GLOBALS.feed.get_general();
render_a_feed(app, ctx, frame, ui, feed, false);
}
FeedKind::Thread(id) => {
let parent = GLOBALS.feed.get_thread_parent(id);
render_a_feed(app, ctx, frame, ui, vec![parent], true);
}
FeedKind::Person(pubkeyhex) => {
let feed = GLOBALS.feed.get_person_feed(pubkeyhex);
render_a_feed(app, ctx, frame, ui, feed, false);
}
}
}
fn render_a_feed(
app: &mut GossipUi,
ctx: &Context,
frame: &mut eframe::Frame,
ui: &mut Ui,
feed: Vec<Id>,
threaded: bool,
) {
ScrollArea::vertical().show(ui, |ui| { ScrollArea::vertical().show(ui, |ui| {
let bgcolor = if ctx.style().visuals.dark_mode { let bgcolor = if ctx.style().visuals.dark_mode {
Color32::BLACK Color32::BLACK
@ -191,11 +219,6 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram
} }
}); });
}); });
} else if app.page == Page::FeedThread {
thread::update(app, ctx, frame, ui);
} else if app.page == Page::FeedPerson {
person::update(app, ctx, frame, ui);
}
} }
fn render_post_maybe_fake( fn render_post_maybe_fake(
@ -238,7 +261,7 @@ fn render_post_maybe_fake(
ui.add_space(height); ui.add_space(height);
// Yes, and we need to fake render threads to get their approx height too. // Yes, and we need to fake render threads to get their approx height too.
if threaded && !as_reply_to && !app.hides.contains(&id) { if threaded && !as_reply_to {
let replies = Globals::get_replies_sync(event.id); let replies = Globals::get_replies_sync(event.id);
for reply_id in replies { for reply_id in replies {
render_post_maybe_fake( render_post_maybe_fake(
@ -352,17 +375,6 @@ fn render_post_actual(
ui.horizontal(|ui| { ui.horizontal(|ui| {
// Indents first (if threaded) // Indents first (if threaded)
if threaded { if threaded {
#[allow(clippy::collapsible_else_if)]
if app.hides.contains(&id) {
if ui.add(Label::new("").sense(Sense::click())).clicked() {
app.hides.retain(|e| *e != id)
}
} else {
if ui.add(Label::new("").sense(Sense::click())).clicked() {
app.hides.push(id);
}
}
let space = 16.0 * (10.0 - (100.0 / (indent as f32 + 10.0))); let space = 16.0 * (10.0 - (100.0 / (indent as f32 + 10.0)));
ui.add_space(space); ui.add_space(space);
if indent > 0 { if indent > 0 {
@ -406,6 +418,10 @@ fn render_post_actual(
ui.with_layout(Layout::right_to_left(Align::TOP), |ui| { ui.with_layout(Layout::right_to_left(Align::TOP), |ui| {
ui.menu_button(RichText::new("").size(28.0), |ui| { ui.menu_button(RichText::new("").size(28.0), |ui| {
if ui.button("View Thread").clicked() {
GLOBALS.feed.set_feed_to_thread(event.id);
app.page = Page::FeedThread;
}
if ui.button("Copy ID").clicked() { if ui.button("Copy ID").clicked() {
ui.output().copied_text = event.id.as_hex_string(); ui.output().copied_text = event.id.as_hex_string();
} }
@ -424,6 +440,11 @@ fn render_post_actual(
} }
}); });
if ui.button("").clicked() {
GLOBALS.feed.set_feed_to_thread(event.id);
app.page = Page::FeedThread;
}
ui.label( ui.label(
RichText::new(crate::date_ago::date_ago(event.created_at)) RichText::new(crate::date_ago::date_ago(event.created_at))
.italics() .italics()
@ -475,7 +496,7 @@ fn render_post_actual(
ui.separator(); ui.separator();
if threaded && !as_reply_to && !app.hides.contains(&id) { if threaded && !as_reply_to {
let replies = Globals::get_replies_sync(event.id); let replies = Globals::get_replies_sync(event.id);
for reply_id in replies { for reply_id in replies {
render_post_maybe_fake( render_post_maybe_fake(

View File

@ -1,7 +0,0 @@
use super::GossipUi;
use eframe::egui;
use egui::{Context, Ui};
pub(super) fn update(_app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {
ui.label("Person TBD");
}

View File

@ -1,7 +0,0 @@
use super::GossipUi;
use eframe::egui;
use egui::{Context, Ui};
pub(super) fn update(_app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {
ui.label("Thread TBD");
}

View File

@ -85,12 +85,9 @@ struct GossipUi {
import_bech32: String, import_bech32: String,
import_hex: String, import_hex: String,
replying_to: Option<Id>, replying_to: Option<Id>,
hides: Vec<Id>,
person_view_pubkey: Option<PublicKeyHex>, person_view_pubkey: Option<PublicKeyHex>,
avatars: HashMap<PublicKeyHex, TextureHandle>, avatars: HashMap<PublicKeyHex, TextureHandle>,
new_relay_url: String, new_relay_url: String,
feed_thread_id: Option<Id>,
feed_person_pubkey: Option<PublicKeyHex>,
} }
impl Drop for GossipUi { impl Drop for GossipUi {
@ -160,12 +157,9 @@ impl GossipUi {
import_bech32: "".to_owned(), import_bech32: "".to_owned(),
import_hex: "".to_owned(), import_hex: "".to_owned(),
replying_to: None, replying_to: None,
hides: Vec::new(),
person_view_pubkey: None, person_view_pubkey: None,
avatars: HashMap::new(), avatars: HashMap::new(),
new_relay_url: "".to_owned(), new_relay_url: "".to_owned(),
feed_thread_id: None,
feed_person_pubkey: None,
} }
} }
} }

View File

@ -1,4 +1,4 @@
use super::GossipUi; use super::{GossipUi, Page};
use crate::comms::BusMessage; use crate::comms::BusMessage;
use crate::db::DbPerson; use crate::db::DbPerson;
use crate::globals::GLOBALS; use crate::globals::GLOBALS;
@ -63,6 +63,11 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra
json_payload: serde_json::to_string(&pubkeyhex).unwrap(), json_payload: serde_json::to_string(&pubkeyhex).unwrap(),
}); });
} }
if ui.button("VIEW THEIR FEED").clicked() {
GLOBALS.feed.set_feed_to_person(pubkeyhex.clone());
app.page = Page::FeedPerson;
}
} }
} }