mirror of
https://github.com/mikedilger/gossip.git
synced 2024-09-19 19:46:50 +00:00
Multiple feeds working
This commit is contained in:
parent
238c429017
commit
611b5ab12c
103
src/feed.rs
103
src/feed.rs
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(
|
@ -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");
|
|
||||||
}
|
|
@ -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");
|
|
||||||
}
|
|
@ -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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user