mirror of
https://github.com/mikedilger/gossip.git
synced 2024-09-19 19:46:50 +00:00
Feed object, only recomputes periodically; comes with a new setting
This commit is contained in:
parent
669bcde3f4
commit
958989b01e
67
src/feed.rs
Normal file
67
src/feed.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use crate::globals::GLOBALS;
|
||||
use nostr_types::{Event, EventKind, Id};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
pub struct Feed {
|
||||
feed: Vec<Id>,
|
||||
|
||||
// We only recompute the feed at specified intervals
|
||||
interval_ms: u32,
|
||||
last_computed: Instant,
|
||||
}
|
||||
|
||||
impl Feed {
|
||||
pub fn new() -> Feed {
|
||||
Feed {
|
||||
feed: Vec::new(),
|
||||
interval_ms: 1000, // Every second, until we load from settings
|
||||
last_computed: Instant::now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&mut self) -> Vec<Id> {
|
||||
let now = Instant::now();
|
||||
if self.last_computed + Duration::from_millis(self.interval_ms as u64) < now {
|
||||
self.recompute();
|
||||
self.last_computed = now;
|
||||
}
|
||||
|
||||
self.feed.clone()
|
||||
}
|
||||
|
||||
fn recompute(&mut self) {
|
||||
let settings = GLOBALS.settings.blocking_read().clone();
|
||||
self.interval_ms = settings.feed_recompute_interval_ms;
|
||||
|
||||
let mut events: Vec<Event> = GLOBALS
|
||||
.events
|
||||
.blocking_read()
|
||||
.iter()
|
||||
.map(|(_, e)| e)
|
||||
.filter(|e| e.kind == EventKind::TextNote)
|
||||
.filter(|e| !GLOBALS.dismissed.blocking_read().contains(&e.id))
|
||||
.filter(|e| {
|
||||
if settings.view_threaded {
|
||||
e.replies_to().is_none()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
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)
|
||||
});
|
||||
} else {
|
||||
events.sort_unstable_by(|a, b| b.created_at.cmp(&a.created_at));
|
||||
}
|
||||
|
||||
self.feed = events.iter().map(|e| e.id).collect();
|
||||
}
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
use crate::comms::BusMessage;
|
||||
use crate::db::{DbEvent, DbPerson, DbPersonRelay, DbRelay};
|
||||
use crate::error::Error;
|
||||
use crate::feed::Feed;
|
||||
use crate::relationship::Relationship;
|
||||
use crate::settings::Settings;
|
||||
use crate::signer::Signer;
|
||||
use async_recursion::async_recursion;
|
||||
use nostr_types::{Event, EventKind, Id, IdHex, PublicKey, PublicKeyHex, Unixtime, Url};
|
||||
use nostr_types::{Event, Id, IdHex, PublicKey, PublicKeyHex, Unixtime, Url};
|
||||
use rusqlite::Connection;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
@ -62,6 +63,9 @@ pub struct Globals {
|
||||
|
||||
/// Dismissed Events
|
||||
pub dismissed: RwLock<Vec<Id>>,
|
||||
|
||||
/// Feed
|
||||
pub feed: Mutex<Feed>,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
@ -88,48 +92,12 @@ lazy_static! {
|
||||
settings: RwLock::new(Settings::default()),
|
||||
signer: RwLock::new(Signer::default()),
|
||||
dismissed: RwLock::new(Vec::new()),
|
||||
feed: Mutex::new(Feed::new()),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Globals {
|
||||
pub fn blocking_get_feed(threaded: bool) -> Vec<Id> {
|
||||
let feed: Vec<Event> = GLOBALS
|
||||
.events
|
||||
.blocking_read()
|
||||
.iter()
|
||||
.map(|(_, e)| e)
|
||||
.filter(|e| e.kind == EventKind::TextNote)
|
||||
.filter(|e| !GLOBALS.dismissed.blocking_read().contains(&e.id))
|
||||
.filter(|e| {
|
||||
if threaded {
|
||||
e.replies_to().is_none()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
Self::sort_feed(feed, threaded)
|
||||
}
|
||||
|
||||
fn sort_feed(mut feed: Vec<Event>, threaded: bool) -> Vec<Id> {
|
||||
if threaded {
|
||||
feed.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)
|
||||
});
|
||||
} else {
|
||||
feed.sort_unstable_by(|a, b| b.created_at.cmp(&a.created_at));
|
||||
}
|
||||
|
||||
feed.iter().map(|e| e.id).collect()
|
||||
}
|
||||
|
||||
pub async fn store_desired_event(id: Id, url: Option<Url>) {
|
||||
let mut desired_events = GLOBALS.desired_events.write().await;
|
||||
desired_events
|
||||
|
@ -8,6 +8,7 @@ mod comms;
|
||||
mod date_ago;
|
||||
mod db;
|
||||
mod error;
|
||||
mod feed;
|
||||
mod globals;
|
||||
mod overlord;
|
||||
mod process;
|
||||
|
@ -13,6 +13,7 @@ pub const DEFAULT_VIEW_THREADED: bool = true;
|
||||
pub const DEFAULT_NUM_RELAYS_PER_PERSON: u8 = 2;
|
||||
pub const DEFAULT_MAX_RELAYS: u8 = 15;
|
||||
pub const DEFAULT_MAX_FPS: u32 = 60;
|
||||
pub const DEFAULT_FEED_RECOMPUTE_INTERVAL_MS: u32 = 1000;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Settings {
|
||||
@ -27,6 +28,7 @@ pub struct Settings {
|
||||
pub public_key: Option<PublicKey>,
|
||||
pub encrypted_private_key: Option<EncryptedPrivateKey>,
|
||||
pub max_fps: u32,
|
||||
pub feed_recompute_interval_ms: u32,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
@ -43,6 +45,7 @@ impl Default for Settings {
|
||||
public_key: None,
|
||||
encrypted_private_key: None,
|
||||
max_fps: DEFAULT_MAX_FPS,
|
||||
feed_recompute_interval_ms: DEFAULT_FEED_RECOMPUTE_INTERVAL_MS,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -93,6 +96,12 @@ impl Settings {
|
||||
}
|
||||
}
|
||||
"max_fps" => settings.max_fps = row.1.parse::<u32>().unwrap_or(DEFAULT_MAX_FPS),
|
||||
"feed_recompute_interval_ms" => {
|
||||
settings.feed_recompute_interval_ms = row
|
||||
.1
|
||||
.parse::<u32>()
|
||||
.unwrap_or(DEFAULT_FEED_RECOMPUTE_INTERVAL_MS)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -109,7 +118,7 @@ impl Settings {
|
||||
('feed_chunk', ?),('overlap', ?),('autofollow', ?),\
|
||||
('view_posts_referred_to', ?),('view_posts_referring_to', ?),\
|
||||
('view_threaded', ?),('num_relays_per_person', ?), \
|
||||
('max_relays', ?),('max_fps', ?)",
|
||||
('max_relays', ?),('max_fps', ?),('feed_recompute_interval_ms', ?)",
|
||||
)?;
|
||||
stmt.execute((
|
||||
self.feed_chunk,
|
||||
@ -129,6 +138,7 @@ impl Settings {
|
||||
self.num_relays_per_person,
|
||||
self.max_relays,
|
||||
self.max_fps,
|
||||
self.feed_recompute_interval_ms,
|
||||
))?;
|
||||
|
||||
// Save private key identity
|
||||
|
@ -7,7 +7,7 @@ use egui::{Align, Color32, Context, Layout, RichText, ScrollArea, Ui, Vec2};
|
||||
use nostr_types::{EventKind, Id};
|
||||
|
||||
pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Frame, ui: &mut Ui) {
|
||||
let feed = Globals::blocking_get_feed(true);
|
||||
let feed = GLOBALS.feed.blocking_lock().get();
|
||||
|
||||
//let screen_rect = ctx.input().screen_rect; // Rect
|
||||
|
||||
|
@ -14,9 +14,10 @@ pub(super) fn update(
|
||||
) {
|
||||
ui.heading("Settings");
|
||||
|
||||
ui.add_space(12.0);
|
||||
ui.separator();
|
||||
ui.add_space(12.0);
|
||||
|
||||
ui.add_space(24.0);
|
||||
ui.heading("How Many Relays to Query");
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
@ -32,7 +33,10 @@ pub(super) fn update(
|
||||
ui.add(Slider::new(&mut app.settings.max_relays, 1..=30).text("relays"));
|
||||
});
|
||||
|
||||
ui.add_space(24.0);
|
||||
ui.add_space(12.0);
|
||||
ui.separator();
|
||||
ui.add_space(12.0);
|
||||
|
||||
ui.heading("How Many Posts to Load");
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
@ -47,13 +51,28 @@ pub(super) fn update(
|
||||
ui.label(secs_to_string(app.settings.overlap));
|
||||
});
|
||||
|
||||
ui.add_space(24.0);
|
||||
ui.heading("Feed Style / Order");
|
||||
ui.add_space(12.0);
|
||||
ui.separator();
|
||||
ui.add_space(12.0);
|
||||
|
||||
ui.heading("Feed");
|
||||
|
||||
ui.checkbox(&mut app.settings.view_threaded, "Threaded feed")
|
||||
.on_hover_text("If selected, replies are under what they reply to and the newest replied-to thread comes first. Otherwise all posts are independent and in time order.");
|
||||
|
||||
ui.add_space(24.0);
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Recompute feed every (milliseconds): ")
|
||||
.on_hover_text(
|
||||
"The UI redraws frequently. We recompute the feed less frequently to conserve CPU. Takes effect when the feed next recomputes.",
|
||||
);
|
||||
ui.add(Slider::new(&mut app.settings.feed_recompute_interval_ms, 250..=5000).text("milliseconds"));
|
||||
});
|
||||
|
||||
ui.add_space(12.0);
|
||||
ui.separator();
|
||||
ui.add_space(12.0);
|
||||
|
||||
ui.heading("What Posts to Include");
|
||||
|
||||
ui.checkbox(
|
||||
@ -67,16 +86,10 @@ pub(super) fn update(
|
||||
ui.checkbox(&mut app.settings.view_posts_referring_to, "View posts referring to posts by people you follow (not yet implemented)")
|
||||
.on_hover_text("Not recommended, as anyone can reply to them and you'll certainly encounter spam this way.");
|
||||
|
||||
ui.add_space(24.0);
|
||||
ui.heading("Miscellaneous");
|
||||
ui.add_space(12.0);
|
||||
ui.separator();
|
||||
ui.add_space(12.0);
|
||||
|
||||
ui.checkbox(
|
||||
&mut app.settings.autofollow,
|
||||
"Autofollow everybody (not yet implemented)",
|
||||
)
|
||||
.on_hover_text("Definately not recommended. In fact we may remove this soon.");
|
||||
|
||||
ui.add_space(24.0);
|
||||
ui.heading("User Interface");
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
@ -102,7 +115,7 @@ pub(super) fn update(
|
||||
}
|
||||
});
|
||||
|
||||
ui.add_space(24.0);
|
||||
ui.add_space(12.0);
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Maximum FPS: ")
|
||||
.on_hover_text(
|
||||
@ -111,7 +124,10 @@ pub(super) fn update(
|
||||
ui.add(Slider::new(&mut app.settings.max_fps, 10..=60).text("Frames per second"));
|
||||
});
|
||||
|
||||
ui.add_space(32.0);
|
||||
ui.add_space(12.0);
|
||||
ui.separator();
|
||||
ui.add_space(24.0);
|
||||
|
||||
ui.with_layout(Layout::top_down(Align::Center), |ui| {
|
||||
if ui.button("SAVE CHANGES").clicked() {
|
||||
let tx = GLOBALS.to_overlord.clone();
|
||||
|
Loading…
Reference in New Issue
Block a user