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::comms::BusMessage;
|
||||||
use crate::db::{DbEvent, DbPerson, DbPersonRelay, DbRelay};
|
use crate::db::{DbEvent, DbPerson, DbPersonRelay, DbRelay};
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use crate::feed::Feed;
|
||||||
use crate::relationship::Relationship;
|
use crate::relationship::Relationship;
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use crate::signer::Signer;
|
use crate::signer::Signer;
|
||||||
use async_recursion::async_recursion;
|
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 rusqlite::Connection;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
@ -62,6 +63,9 @@ pub struct Globals {
|
|||||||
|
|
||||||
/// Dismissed Events
|
/// Dismissed Events
|
||||||
pub dismissed: RwLock<Vec<Id>>,
|
pub dismissed: RwLock<Vec<Id>>,
|
||||||
|
|
||||||
|
/// Feed
|
||||||
|
pub feed: Mutex<Feed>,
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@ -88,48 +92,12 @@ lazy_static! {
|
|||||||
settings: RwLock::new(Settings::default()),
|
settings: RwLock::new(Settings::default()),
|
||||||
signer: RwLock::new(Signer::default()),
|
signer: RwLock::new(Signer::default()),
|
||||||
dismissed: RwLock::new(Vec::new()),
|
dismissed: RwLock::new(Vec::new()),
|
||||||
|
feed: Mutex::new(Feed::new()),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Globals {
|
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>) {
|
pub async fn store_desired_event(id: Id, url: Option<Url>) {
|
||||||
let mut desired_events = GLOBALS.desired_events.write().await;
|
let mut desired_events = GLOBALS.desired_events.write().await;
|
||||||
desired_events
|
desired_events
|
||||||
|
@ -8,6 +8,7 @@ mod comms;
|
|||||||
mod date_ago;
|
mod date_ago;
|
||||||
mod db;
|
mod db;
|
||||||
mod error;
|
mod error;
|
||||||
|
mod feed;
|
||||||
mod globals;
|
mod globals;
|
||||||
mod overlord;
|
mod overlord;
|
||||||
mod process;
|
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_NUM_RELAYS_PER_PERSON: u8 = 2;
|
||||||
pub const DEFAULT_MAX_RELAYS: u8 = 15;
|
pub const DEFAULT_MAX_RELAYS: u8 = 15;
|
||||||
pub const DEFAULT_MAX_FPS: u32 = 60;
|
pub const DEFAULT_MAX_FPS: u32 = 60;
|
||||||
|
pub const DEFAULT_FEED_RECOMPUTE_INTERVAL_MS: u32 = 1000;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
@ -27,6 +28,7 @@ pub struct Settings {
|
|||||||
pub public_key: Option<PublicKey>,
|
pub public_key: Option<PublicKey>,
|
||||||
pub encrypted_private_key: Option<EncryptedPrivateKey>,
|
pub encrypted_private_key: Option<EncryptedPrivateKey>,
|
||||||
pub max_fps: u32,
|
pub max_fps: u32,
|
||||||
|
pub feed_recompute_interval_ms: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Settings {
|
impl Default for Settings {
|
||||||
@ -43,6 +45,7 @@ impl Default for Settings {
|
|||||||
public_key: None,
|
public_key: None,
|
||||||
encrypted_private_key: None,
|
encrypted_private_key: None,
|
||||||
max_fps: DEFAULT_MAX_FPS,
|
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),
|
"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', ?),\
|
('feed_chunk', ?),('overlap', ?),('autofollow', ?),\
|
||||||
('view_posts_referred_to', ?),('view_posts_referring_to', ?),\
|
('view_posts_referred_to', ?),('view_posts_referring_to', ?),\
|
||||||
('view_threaded', ?),('num_relays_per_person', ?), \
|
('view_threaded', ?),('num_relays_per_person', ?), \
|
||||||
('max_relays', ?),('max_fps', ?)",
|
('max_relays', ?),('max_fps', ?),('feed_recompute_interval_ms', ?)",
|
||||||
)?;
|
)?;
|
||||||
stmt.execute((
|
stmt.execute((
|
||||||
self.feed_chunk,
|
self.feed_chunk,
|
||||||
@ -129,6 +138,7 @@ impl Settings {
|
|||||||
self.num_relays_per_person,
|
self.num_relays_per_person,
|
||||||
self.max_relays,
|
self.max_relays,
|
||||||
self.max_fps,
|
self.max_fps,
|
||||||
|
self.feed_recompute_interval_ms,
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
// Save private key identity
|
// Save private key identity
|
||||||
|
@ -7,7 +7,7 @@ use egui::{Align, Color32, Context, Layout, RichText, ScrollArea, Ui, Vec2};
|
|||||||
use nostr_types::{EventKind, Id};
|
use nostr_types::{EventKind, Id};
|
||||||
|
|
||||||
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 feed = Globals::blocking_get_feed(true);
|
let feed = GLOBALS.feed.blocking_lock().get();
|
||||||
|
|
||||||
//let screen_rect = ctx.input().screen_rect; // Rect
|
//let screen_rect = ctx.input().screen_rect; // Rect
|
||||||
|
|
||||||
|
@ -14,9 +14,10 @@ pub(super) fn update(
|
|||||||
) {
|
) {
|
||||||
ui.heading("Settings");
|
ui.heading("Settings");
|
||||||
|
|
||||||
|
ui.add_space(12.0);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
ui.add_space(12.0);
|
||||||
|
|
||||||
ui.add_space(24.0);
|
|
||||||
ui.heading("How Many Relays to Query");
|
ui.heading("How Many Relays to Query");
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
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(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.heading("How Many Posts to Load");
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
@ -47,13 +51,28 @@ pub(super) fn update(
|
|||||||
ui.label(secs_to_string(app.settings.overlap));
|
ui.label(secs_to_string(app.settings.overlap));
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.add_space(24.0);
|
ui.add_space(12.0);
|
||||||
ui.heading("Feed Style / Order");
|
ui.separator();
|
||||||
|
ui.add_space(12.0);
|
||||||
|
|
||||||
|
ui.heading("Feed");
|
||||||
|
|
||||||
ui.checkbox(&mut app.settings.view_threaded, "Threaded 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.");
|
.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.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.heading("What Posts to Include");
|
||||||
|
|
||||||
ui.checkbox(
|
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)")
|
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.");
|
.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.add_space(12.0);
|
||||||
ui.heading("Miscellaneous");
|
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.heading("User Interface");
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
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.horizontal(|ui| {
|
||||||
ui.label("Maximum FPS: ")
|
ui.label("Maximum FPS: ")
|
||||||
.on_hover_text(
|
.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(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| {
|
ui.with_layout(Layout::top_down(Align::Center), |ui| {
|
||||||
if ui.button("SAVE CHANGES").clicked() {
|
if ui.button("SAVE CHANGES").clicked() {
|
||||||
let tx = GLOBALS.to_overlord.clone();
|
let tx = GLOBALS.to_overlord.clone();
|
||||||
|
Loading…
Reference in New Issue
Block a user