mirror of
https://github.com/mikedilger/gossip.git
synced 2024-09-19 19:46:50 +00:00
Settings overhaul: put in Globals, load syncronously at startup; load/save as a unit
This commit is contained in:
parent
422c60062b
commit
f1f905e595
@ -30,7 +30,7 @@ use tracing::info;
|
|||||||
|
|
||||||
// This sets up the database
|
// This sets up the database
|
||||||
#[allow(clippy::or_fun_call)]
|
#[allow(clippy::or_fun_call)]
|
||||||
pub async fn setup_database() -> Result<(), Error> {
|
pub fn setup_database() -> Result<(), Error> {
|
||||||
let mut data_dir = dirs::data_dir()
|
let mut data_dir = dirs::data_dir()
|
||||||
.ok_or::<Error>("Cannot find a directory to store application data.".into())?;
|
.ok_or::<Error>("Cannot find a directory to store application data.".into())?;
|
||||||
data_dir.push("gossip");
|
data_dir.push("gossip");
|
||||||
@ -51,18 +51,18 @@ pub async fn setup_database() -> Result<(), Error> {
|
|||||||
|
|
||||||
// Save the connection globally
|
// Save the connection globally
|
||||||
{
|
{
|
||||||
let mut db = GLOBALS.db.lock().await;
|
let mut db = GLOBALS.db.blocking_lock();
|
||||||
*db = Some(connection);
|
*db = Some(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check and upgrade our data schema
|
// Check and upgrade our data schema
|
||||||
check_and_upgrade().await?;
|
check_and_upgrade()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_and_upgrade() -> Result<(), Error> {
|
fn check_and_upgrade() -> Result<(), Error> {
|
||||||
let maybe_db = GLOBALS.db.lock().await;
|
let maybe_db = GLOBALS.db.blocking_lock();
|
||||||
let db = maybe_db.as_ref().unwrap();
|
let db = maybe_db.as_ref().unwrap();
|
||||||
|
|
||||||
// Load the current version
|
// Load the current version
|
||||||
|
@ -63,6 +63,7 @@ impl DbSetting {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub async fn fetch_setting_u64_or_default(key: &str, default: u64) -> Result<u64, Error> {
|
pub async fn fetch_setting_u64_or_default(key: &str, default: u64) -> Result<u64, Error> {
|
||||||
let db_settings = DbSetting::fetch(Some(&format!("key='{}'", key))).await?;
|
let db_settings = DbSetting::fetch(Some(&format!("key='{}'", key))).await?;
|
||||||
|
|
||||||
@ -108,6 +109,7 @@ impl DbSetting {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub async fn update<T: ToSql + Send + 'static>(key: String, value: T) -> Result<(), Error> {
|
pub async fn update<T: ToSql + Send + 'static>(key: String, value: T) -> Result<(), Error> {
|
||||||
let sql = "UPDATE settings SET value=? WHERE key=?";
|
let sql = "UPDATE settings SET value=? WHERE key=?";
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ use crate::comms::BusMessage;
|
|||||||
use crate::db::{DbPerson, DbPersonRelay, DbRelay};
|
use crate::db::{DbPerson, DbPersonRelay, DbRelay};
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::feed_event::FeedEvent;
|
use crate::feed_event::FeedEvent;
|
||||||
|
use crate::settings::Settings;
|
||||||
use nostr_proto::{Event, EventKind, Id, Metadata, PublicKey, PublicKeyHex, Tag, Unixtime, Url};
|
use nostr_proto::{Event, EventKind, Id, Metadata, PublicKey, PublicKeyHex, Tag, Unixtime, Url};
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -40,6 +41,9 @@ pub struct Globals {
|
|||||||
/// Whether or not we have a saved private key and need the password to unlock it
|
/// Whether or not we have a saved private key and need the password to unlock it
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub need_password: AtomicBool,
|
pub need_password: AtomicBool,
|
||||||
|
|
||||||
|
/// Settings
|
||||||
|
pub settings: Mutex<Settings>,
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@ -60,6 +64,7 @@ lazy_static! {
|
|||||||
desired_events: Mutex::new(HashMap::new()),
|
desired_events: Mutex::new(HashMap::new()),
|
||||||
people: Mutex::new(HashMap::new()),
|
people: Mutex::new(HashMap::new()),
|
||||||
need_password: AtomicBool::new(false),
|
need_password: AtomicBool::new(false),
|
||||||
|
settings: Mutex::new(Settings::default()),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
11
src/main.rs
11
src/main.rs
@ -20,9 +20,16 @@ use crate::globals::GLOBALS;
|
|||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use std::{mem, thread};
|
use std::{mem, thread};
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), Error> {
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
|
// Setup the database (possibly create, possibly upgrade)
|
||||||
|
crate::db::setup_database()?;
|
||||||
|
|
||||||
|
// Load settings
|
||||||
|
let settings = crate::settings::Settings::blocking_load()?;
|
||||||
|
*GLOBALS.settings.blocking_lock() = settings;
|
||||||
|
|
||||||
// Start async code
|
// Start async code
|
||||||
// We do this on a separate thread because egui is most portable by
|
// We do this on a separate thread because egui is most portable by
|
||||||
// being on the main thread.
|
// being on the main thread.
|
||||||
@ -42,6 +49,8 @@ fn main() {
|
|||||||
|
|
||||||
// Wait for the async thread to complete
|
// Wait for the async thread to complete
|
||||||
async_thread.join().unwrap();
|
async_thread.join().unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tokio_main() {
|
async fn tokio_main() {
|
||||||
|
@ -6,7 +6,6 @@ use crate::comms::BusMessage;
|
|||||||
use crate::db::{DbPersonRelay, DbRelay};
|
use crate::db::{DbPersonRelay, DbRelay};
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::globals::GLOBALS;
|
use crate::globals::GLOBALS;
|
||||||
use crate::settings::Settings;
|
|
||||||
use futures::{SinkExt, StreamExt};
|
use futures::{SinkExt, StreamExt};
|
||||||
use futures_util::stream::{SplitSink, SplitStream};
|
use futures_util::stream::{SplitSink, SplitStream};
|
||||||
use http::Uri;
|
use http::Uri;
|
||||||
@ -25,7 +24,6 @@ pub struct Minion {
|
|||||||
pubkeys: Vec<PublicKeyHex>,
|
pubkeys: Vec<PublicKeyHex>,
|
||||||
to_overlord: UnboundedSender<BusMessage>,
|
to_overlord: UnboundedSender<BusMessage>,
|
||||||
from_overlord: Receiver<BusMessage>,
|
from_overlord: Receiver<BusMessage>,
|
||||||
settings: Settings,
|
|
||||||
dbrelay: DbRelay,
|
dbrelay: DbRelay,
|
||||||
nip11: Option<RelayInformationDocument>,
|
nip11: Option<RelayInformationDocument>,
|
||||||
stream: Option<SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>>,
|
stream: Option<SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>>,
|
||||||
@ -37,7 +35,6 @@ impl Minion {
|
|||||||
pub async fn new(url: Url, pubkeys: Vec<PublicKeyHex>) -> Result<Minion, Error> {
|
pub async fn new(url: Url, pubkeys: Vec<PublicKeyHex>) -> Result<Minion, Error> {
|
||||||
let to_overlord = GLOBALS.to_overlord.clone();
|
let to_overlord = GLOBALS.to_overlord.clone();
|
||||||
let from_overlord = GLOBALS.to_minions.subscribe();
|
let from_overlord = GLOBALS.to_minions.subscribe();
|
||||||
let settings = Settings::load().await?;
|
|
||||||
let dbrelay = match DbRelay::fetch_one(&url).await? {
|
let dbrelay = match DbRelay::fetch_one(&url).await? {
|
||||||
Some(dbrelay) => dbrelay,
|
Some(dbrelay) => dbrelay,
|
||||||
None => {
|
None => {
|
||||||
@ -52,7 +49,6 @@ impl Minion {
|
|||||||
pubkeys,
|
pubkeys,
|
||||||
to_overlord,
|
to_overlord,
|
||||||
from_overlord,
|
from_overlord,
|
||||||
settings,
|
|
||||||
dbrelay,
|
dbrelay,
|
||||||
nip11: None,
|
nip11: None,
|
||||||
stream: None,
|
stream: None,
|
||||||
@ -224,7 +220,7 @@ impl Minion {
|
|||||||
info!("Websocket listener {} shutting down", &self.url);
|
info!("Websocket listener {} shutting down", &self.url);
|
||||||
keepgoing = false;
|
keepgoing = false;
|
||||||
} else if &*bus_message.kind == "settings_changed" {
|
} else if &*bus_message.kind == "settings_changed" {
|
||||||
self.settings = serde_json::from_str(&bus_message.json_payload)?;
|
// TBD: possibly redo filters based on overlap, feed_chunk, etc.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -269,12 +265,14 @@ impl Minion {
|
|||||||
let mut special_since: i64 =
|
let mut special_since: i64 =
|
||||||
DbPersonRelay::fetch_oldest_last_fetched(&self.pubkeys, &self.url.0).await? as i64;
|
DbPersonRelay::fetch_oldest_last_fetched(&self.pubkeys, &self.url.0).await? as i64;
|
||||||
|
|
||||||
|
let settings = GLOBALS.settings.lock().await.clone();
|
||||||
|
|
||||||
// Subtract overlap to avoid gaps due to clock sync and event
|
// Subtract overlap to avoid gaps due to clock sync and event
|
||||||
// propogation delay
|
// propogation delay
|
||||||
special_since -= self.settings.overlap as i64;
|
special_since -= settings.overlap as i64;
|
||||||
|
|
||||||
// For feed related events, don't look back more than one feed_chunk ago
|
// For feed related events, don't look back more than one feed_chunk ago
|
||||||
let one_feedchunk_ago = Unixtime::now().unwrap().0 - self.settings.feed_chunk as i64;
|
let one_feedchunk_ago = Unixtime::now().unwrap().0 - settings.feed_chunk as i64;
|
||||||
let feed_since = special_since.max(one_feedchunk_ago);
|
let feed_since = special_since.max(one_feedchunk_ago);
|
||||||
|
|
||||||
(Unixtime(feed_since), Unixtime(special_since))
|
(Unixtime(feed_since), Unixtime(special_since))
|
||||||
|
@ -60,12 +60,6 @@ impl Overlord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run_inner(&mut self) -> Result<(), Error> {
|
pub async fn run_inner(&mut self) -> Result<(), Error> {
|
||||||
// Setup the database (possibly create, possibly upgrade)
|
|
||||||
crate::db::setup_database().await?;
|
|
||||||
|
|
||||||
// Load settings
|
|
||||||
self.settings = Settings::load().await?;
|
|
||||||
|
|
||||||
// Check for a private key
|
// Check for a private key
|
||||||
if DbSetting::fetch_setting("user_private_key")
|
if DbSetting::fetch_setting("user_private_key")
|
||||||
.await?
|
.await?
|
||||||
@ -82,7 +76,7 @@ impl Overlord {
|
|||||||
// FIXME - if this needs doing, it should be done dynamically as
|
// FIXME - if this needs doing, it should be done dynamically as
|
||||||
// new people are encountered, not batch-style on startup.
|
// new people are encountered, not batch-style on startup.
|
||||||
// Create a person record for every person seen, possibly autofollow
|
// Create a person record for every person seen, possibly autofollow
|
||||||
DbPerson::populate_new_people(self.settings.autofollow != 0).await?;
|
DbPerson::populate_new_people(self.settings.autofollow).await?;
|
||||||
|
|
||||||
// FIXME - if this needs doing, it should be done dynamically as
|
// FIXME - if this needs doing, it should be done dynamically as
|
||||||
// new people are encountered, not batch-style on startup.
|
// new people are encountered, not batch-style on startup.
|
||||||
|
@ -1,16 +1,24 @@
|
|||||||
use crate::db::DbSetting;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use crate::globals::GLOBALS;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub const DEFAULT_FEED_CHUNK: u64 = 43200; // 12 hours
|
pub const DEFAULT_FEED_CHUNK: u64 = 43200; // 12 hours
|
||||||
pub const DEFAULT_OVERLAP: u64 = 600; // 10 minutes
|
pub const DEFAULT_OVERLAP: u64 = 600; // 10 minutes
|
||||||
pub const DEFAULT_AUTOFOLLOW: u64 = 0;
|
pub const DEFAULT_AUTOFOLLOW: bool = false;
|
||||||
|
pub const DEFAULT_VIEW_POSTS_REFERRED_TO: bool = true;
|
||||||
|
pub const DEFAULT_VIEW_POSTS_REFERRING_TO: bool = false;
|
||||||
|
pub const DEFAULT_VIEW_THREADED: bool = true;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
|
// user_private_key
|
||||||
|
// version
|
||||||
pub feed_chunk: u64,
|
pub feed_chunk: u64,
|
||||||
pub overlap: u64,
|
pub overlap: u64,
|
||||||
pub autofollow: u64,
|
pub autofollow: bool,
|
||||||
|
pub view_posts_referred_to: bool,
|
||||||
|
pub view_posts_referring_to: bool,
|
||||||
|
pub view_threaded: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Settings {
|
impl Default for Settings {
|
||||||
@ -18,33 +26,75 @@ impl Default for Settings {
|
|||||||
Settings {
|
Settings {
|
||||||
feed_chunk: DEFAULT_FEED_CHUNK,
|
feed_chunk: DEFAULT_FEED_CHUNK,
|
||||||
overlap: DEFAULT_OVERLAP,
|
overlap: DEFAULT_OVERLAP,
|
||||||
autofollow: 0,
|
autofollow: DEFAULT_AUTOFOLLOW,
|
||||||
|
view_posts_referred_to: DEFAULT_VIEW_POSTS_REFERRED_TO,
|
||||||
|
view_posts_referring_to: DEFAULT_VIEW_POSTS_REFERRING_TO,
|
||||||
|
view_threaded: DEFAULT_VIEW_THREADED,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Settings {
|
impl Settings {
|
||||||
pub async fn load() -> Result<Settings, Error> {
|
pub fn blocking_load() -> Result<Settings, Error> {
|
||||||
let feed_chunk =
|
let mut settings = Settings::default();
|
||||||
DbSetting::fetch_setting_u64_or_default("feed_chunk", DEFAULT_FEED_CHUNK).await?;
|
|
||||||
|
|
||||||
let overlap = DbSetting::fetch_setting_u64_or_default("overlap", DEFAULT_OVERLAP).await?;
|
let maybe_db = GLOBALS.db.blocking_lock();
|
||||||
|
let db = maybe_db.as_ref().unwrap();
|
||||||
|
|
||||||
let autofollow =
|
let mut stmt = db.prepare("SELECT key, value FROM settings")?;
|
||||||
DbSetting::fetch_setting_u64_or_default("autofollow", DEFAULT_AUTOFOLLOW).await?;
|
|
||||||
|
|
||||||
Ok(Settings {
|
let rows = stmt.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?;
|
||||||
feed_chunk,
|
|
||||||
overlap,
|
let numstr_to_bool = |s: String| -> bool { &s == "1" };
|
||||||
autofollow,
|
|
||||||
})
|
for row in rows {
|
||||||
|
let row: (String, String) = row?;
|
||||||
|
match &*row.0 {
|
||||||
|
"feed_chunk" => {
|
||||||
|
settings.feed_chunk = row.1.parse::<u64>().unwrap_or(DEFAULT_FEED_CHUNK)
|
||||||
|
}
|
||||||
|
"overlap" => settings.overlap = row.1.parse::<u64>().unwrap_or(DEFAULT_OVERLAP),
|
||||||
|
"autofollow" => settings.autofollow = numstr_to_bool(row.1),
|
||||||
|
"view_posts_referred_to" => settings.view_posts_referred_to = numstr_to_bool(row.1),
|
||||||
|
"view_posts_referring_to" => {
|
||||||
|
settings.view_posts_referring_to = numstr_to_bool(row.1)
|
||||||
|
}
|
||||||
|
"view_threaded" => settings.view_threaded = numstr_to_bool(row.1),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub async fn save(&self) -> Result<(), Error> {
|
pub async fn save(&self) -> Result<(), Error> {
|
||||||
DbSetting::update("feed_chunk".to_string(), self.feed_chunk).await?;
|
let maybe_db = GLOBALS.db.lock().await;
|
||||||
DbSetting::update("overlap".to_string(), self.overlap).await?;
|
let db = maybe_db.as_ref().unwrap();
|
||||||
DbSetting::update("autofollow".to_string(), self.autofollow).await?;
|
|
||||||
|
let mut stmt = db.prepare(
|
||||||
|
"REPLACE INTO settings (key, value) VALUES \
|
||||||
|
('feed_chunk', ?),('overlap', ?),('autofollow', ?),\
|
||||||
|
('view_posts_referred_to', ?),('view_posts_referring_to', ?),\
|
||||||
|
('view_threaded', ?)",
|
||||||
|
)?;
|
||||||
|
stmt.execute((
|
||||||
|
self.feed_chunk,
|
||||||
|
self.overlap,
|
||||||
|
if self.autofollow { "1" } else { "0" },
|
||||||
|
if self.view_posts_referred_to {
|
||||||
|
"1"
|
||||||
|
} else {
|
||||||
|
"0"
|
||||||
|
},
|
||||||
|
if self.view_posts_referring_to {
|
||||||
|
"1"
|
||||||
|
} else {
|
||||||
|
"0"
|
||||||
|
},
|
||||||
|
if self.view_threaded { "1" } else { "0" },
|
||||||
|
))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ mod you;
|
|||||||
|
|
||||||
use crate::about::About;
|
use crate::about::About;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use crate::globals::GLOBALS;
|
||||||
use eframe::{egui, IconData, Theme};
|
use eframe::{egui, IconData, Theme};
|
||||||
use egui::{ColorImage, Context, ImageData, TextureHandle, TextureOptions};
|
use egui::{ColorImage, Context, ImageData, TextureHandle, TextureOptions};
|
||||||
|
|
||||||
@ -101,13 +102,15 @@ impl GossipUi {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let threaded = GLOBALS.settings.blocking_lock().view_threaded;
|
||||||
|
|
||||||
GossipUi {
|
GossipUi {
|
||||||
page: Page::Feed,
|
page: Page::Feed,
|
||||||
about: crate::about::about(),
|
about: crate::about::about(),
|
||||||
icon: icon_texture_handle,
|
icon: icon_texture_handle,
|
||||||
placeholder_avatar: placeholder_avatar_texture_handle,
|
placeholder_avatar: placeholder_avatar_texture_handle,
|
||||||
draft: "".to_owned(),
|
draft: "".to_owned(),
|
||||||
threaded: true,
|
threaded,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user