Settings overhaul: put in Globals, load syncronously at startup; load/save as a unit

This commit is contained in:
Mike Dilger 2022-12-23 17:11:10 +13:00
parent 422c60062b
commit f1f905e595
8 changed files with 100 additions and 39 deletions

View File

@ -30,7 +30,7 @@ use tracing::info;
// This sets up the database
#[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()
.ok_or::<Error>("Cannot find a directory to store application data.".into())?;
data_dir.push("gossip");
@ -51,18 +51,18 @@ pub async fn setup_database() -> Result<(), Error> {
// Save the connection globally
{
let mut db = GLOBALS.db.lock().await;
let mut db = GLOBALS.db.blocking_lock();
*db = Some(connection);
}
// Check and upgrade our data schema
check_and_upgrade().await?;
check_and_upgrade()?;
Ok(())
}
async fn check_and_upgrade() -> Result<(), Error> {
let maybe_db = GLOBALS.db.lock().await;
fn check_and_upgrade() -> Result<(), Error> {
let maybe_db = GLOBALS.db.blocking_lock();
let db = maybe_db.as_ref().unwrap();
// Load the current version

View File

@ -63,6 +63,7 @@ impl DbSetting {
}
}
#[allow(dead_code)]
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?;
@ -108,6 +109,7 @@ impl DbSetting {
Ok(())
}
#[allow(dead_code)]
pub async fn update<T: ToSql + Send + 'static>(key: String, value: T) -> Result<(), Error> {
let sql = "UPDATE settings SET value=? WHERE key=?";

View File

@ -2,6 +2,7 @@ use crate::comms::BusMessage;
use crate::db::{DbPerson, DbPersonRelay, DbRelay};
use crate::error::Error;
use crate::feed_event::FeedEvent;
use crate::settings::Settings;
use nostr_proto::{Event, EventKind, Id, Metadata, PublicKey, PublicKeyHex, Tag, Unixtime, Url};
use rusqlite::Connection;
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
#[allow(dead_code)]
pub need_password: AtomicBool,
/// Settings
pub settings: Mutex<Settings>,
}
lazy_static! {
@ -60,6 +64,7 @@ lazy_static! {
desired_events: Mutex::new(HashMap::new()),
people: Mutex::new(HashMap::new()),
need_password: AtomicBool::new(false),
settings: Mutex::new(Settings::default()),
}
};
}

View File

@ -20,9 +20,16 @@ use crate::globals::GLOBALS;
use std::ops::DerefMut;
use std::{mem, thread};
fn main() {
fn main() -> Result<(), Error> {
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
// We do this on a separate thread because egui is most portable by
// being on the main thread.
@ -42,6 +49,8 @@ fn main() {
// Wait for the async thread to complete
async_thread.join().unwrap();
Ok(())
}
async fn tokio_main() {

View File

@ -6,7 +6,6 @@ use crate::comms::BusMessage;
use crate::db::{DbPersonRelay, DbRelay};
use crate::error::Error;
use crate::globals::GLOBALS;
use crate::settings::Settings;
use futures::{SinkExt, StreamExt};
use futures_util::stream::{SplitSink, SplitStream};
use http::Uri;
@ -25,7 +24,6 @@ pub struct Minion {
pubkeys: Vec<PublicKeyHex>,
to_overlord: UnboundedSender<BusMessage>,
from_overlord: Receiver<BusMessage>,
settings: Settings,
dbrelay: DbRelay,
nip11: Option<RelayInformationDocument>,
stream: Option<SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>>,
@ -37,7 +35,6 @@ impl Minion {
pub async fn new(url: Url, pubkeys: Vec<PublicKeyHex>) -> Result<Minion, Error> {
let to_overlord = GLOBALS.to_overlord.clone();
let from_overlord = GLOBALS.to_minions.subscribe();
let settings = Settings::load().await?;
let dbrelay = match DbRelay::fetch_one(&url).await? {
Some(dbrelay) => dbrelay,
None => {
@ -52,7 +49,6 @@ impl Minion {
pubkeys,
to_overlord,
from_overlord,
settings,
dbrelay,
nip11: None,
stream: None,
@ -224,7 +220,7 @@ impl Minion {
info!("Websocket listener {} shutting down", &self.url);
keepgoing = false;
} 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 =
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
// 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
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);
(Unixtime(feed_since), Unixtime(special_since))

View File

@ -60,12 +60,6 @@ impl Overlord {
}
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
if DbSetting::fetch_setting("user_private_key")
.await?
@ -82,7 +76,7 @@ impl Overlord {
// FIXME - if this needs doing, it should be done dynamically as
// new people are encountered, not batch-style on startup.
// 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
// new people are encountered, not batch-style on startup.

View File

@ -1,16 +1,24 @@
use crate::db::DbSetting;
use crate::error::Error;
use crate::globals::GLOBALS;
use serde::{Deserialize, Serialize};
pub const DEFAULT_FEED_CHUNK: u64 = 43200; // 12 hours
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)]
pub struct Settings {
// user_private_key
// version
pub feed_chunk: 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 {
@ -18,33 +26,75 @@ impl Default for Settings {
Settings {
feed_chunk: DEFAULT_FEED_CHUNK,
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 {
pub async fn load() -> Result<Settings, Error> {
let feed_chunk =
DbSetting::fetch_setting_u64_or_default("feed_chunk", DEFAULT_FEED_CHUNK).await?;
pub fn blocking_load() -> Result<Settings, Error> {
let mut settings = Settings::default();
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 =
DbSetting::fetch_setting_u64_or_default("autofollow", DEFAULT_AUTOFOLLOW).await?;
let mut stmt = db.prepare("SELECT key, value FROM settings")?;
Ok(Settings {
feed_chunk,
overlap,
autofollow,
})
let rows = stmt.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?;
let numstr_to_bool = |s: String| -> bool { &s == "1" };
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)]
pub async fn save(&self) -> Result<(), Error> {
DbSetting::update("feed_chunk".to_string(), self.feed_chunk).await?;
DbSetting::update("overlap".to_string(), self.overlap).await?;
DbSetting::update("autofollow".to_string(), self.autofollow).await?;
let maybe_db = GLOBALS.db.lock().await;
let db = maybe_db.as_ref().unwrap();
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(())
}
}

View File

@ -9,6 +9,7 @@ mod you;
use crate::about::About;
use crate::error::Error;
use crate::globals::GLOBALS;
use eframe::{egui, IconData, Theme};
use egui::{ColorImage, Context, ImageData, TextureHandle, TextureOptions};
@ -101,13 +102,15 @@ impl GossipUi {
)
};
let threaded = GLOBALS.settings.blocking_lock().view_threaded;
GossipUi {
page: Page::Feed,
about: crate::about::about(),
icon: icon_texture_handle,
placeholder_avatar: placeholder_avatar_texture_handle,
draft: "".to_owned(),
threaded: true,
threaded,
}
}
}