chore: improve performance (#42)
* use uniform list for rooms list * move profile cache to outside gpui context * update comment * refactor * refactor * . * . * add avatar component * . * refactor * .
This commit is contained in:
@@ -10,3 +10,4 @@ dirs.workspace = true
|
||||
smol.workspace = true
|
||||
|
||||
whoami = "1.5.2"
|
||||
rustls = "0.23.23"
|
||||
|
||||
@@ -27,12 +27,8 @@ pub const DEFAULT_MODAL_WIDTH: f32 = 420.;
|
||||
/// Default width of the sidebar.
|
||||
pub const DEFAULT_SIDEBAR_WIDTH: f32 = 280.;
|
||||
|
||||
/// Total remote images will be cached
|
||||
pub const IMAGE_CACHE_LIMIT: usize = 50;
|
||||
|
||||
/// Image Resizer Service.
|
||||
/// Use for resize all remote images (ex: avatar, banner,...) on-the-fly.
|
||||
pub const IMAGE_SERVICE: &str = "https://wsrv.nl";
|
||||
/// Image Resize Service
|
||||
pub const IMAGE_RESIZE_SERVICE: &str = "https://wsrv.nl";
|
||||
|
||||
/// NIP96 Media Server.
|
||||
pub const NIP96_SERVER: &str = "https://nostrmedia.com";
|
||||
|
||||
@@ -1,37 +1,110 @@
|
||||
use nostr_sdk::prelude::*;
|
||||
use paths::nostr_file;
|
||||
use smol::lock::RwLock;
|
||||
|
||||
use std::{sync::OnceLock, time::Duration};
|
||||
use std::{collections::BTreeMap, sync::OnceLock, time::Duration};
|
||||
|
||||
pub mod constants;
|
||||
pub mod paths;
|
||||
|
||||
static CLIENT: OnceLock<Client> = OnceLock::new();
|
||||
static CLIENT_KEYS: OnceLock<Keys> = OnceLock::new();
|
||||
/// Represents the global state of the Nostr client, including:
|
||||
/// - The Nostr client instance
|
||||
/// - Client keys
|
||||
/// - A cache of user profiles (metadata)
|
||||
pub struct NostrState {
|
||||
keys: Keys,
|
||||
client: Client,
|
||||
cache_profiles: RwLock<BTreeMap<PublicKey, Option<Metadata>>>,
|
||||
}
|
||||
|
||||
/// Nostr Client instance
|
||||
/// Global singleton instance of NostrState
|
||||
static GLOBAL_STATE: OnceLock<NostrState> = OnceLock::new();
|
||||
|
||||
/// Initializes and returns a new NostrState instance with:
|
||||
/// - LMDB database backend
|
||||
/// - Default client options (gossip enabled, 800ms max avg latency)
|
||||
/// - Newly generated keys
|
||||
/// - Empty profile cache
|
||||
pub fn init_global_state() -> NostrState {
|
||||
// rustls uses the `aws_lc_rs` provider by default
|
||||
// This only errors if the default provider has already
|
||||
// been installed. We can ignore this `Result`.
|
||||
rustls::crypto::aws_lc_rs::default_provider()
|
||||
.install_default()
|
||||
.ok();
|
||||
|
||||
// Setup database
|
||||
let db_path = nostr_file();
|
||||
let lmdb = NostrLMDB::open(db_path).expect("Database is NOT initialized");
|
||||
|
||||
// Client options
|
||||
let opts = Options::new()
|
||||
.gossip(true)
|
||||
.max_avg_latency(Duration::from_millis(800));
|
||||
|
||||
NostrState {
|
||||
client: ClientBuilder::default().database(lmdb).opts(opts).build(),
|
||||
keys: Keys::generate(),
|
||||
cache_profiles: RwLock::new(BTreeMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the global Nostr client instance.
|
||||
///
|
||||
/// Initializes the global state if it hasn't been initialized yet.
|
||||
pub fn get_client() -> &'static Client {
|
||||
CLIENT.get_or_init(|| {
|
||||
// Setup database
|
||||
let db_path = nostr_file();
|
||||
let lmdb = NostrLMDB::open(db_path).expect("Database is NOT initialized");
|
||||
|
||||
// Client options
|
||||
let opts = Options::new()
|
||||
// NIP-65
|
||||
// Coop is don't really need to enable this option,
|
||||
// but this will help the client discover user's messaging relays efficiently.
|
||||
.gossip(true)
|
||||
// Skip all very slow relays
|
||||
// Note: max delay is 800ms
|
||||
.max_avg_latency(Duration::from_millis(800));
|
||||
|
||||
// Setup Nostr Client
|
||||
ClientBuilder::default().database(lmdb).opts(opts).build()
|
||||
})
|
||||
&GLOBAL_STATE.get_or_init(init_global_state).client
|
||||
}
|
||||
|
||||
/// Client Keys
|
||||
/// Returns a reference to the client's cryptographic keys.
|
||||
///
|
||||
/// Initializes the global state if it hasn't been initialized yet.
|
||||
pub fn get_client_keys() -> &'static Keys {
|
||||
CLIENT_KEYS.get_or_init(Keys::generate)
|
||||
&GLOBAL_STATE.get_or_init(init_global_state).keys
|
||||
}
|
||||
|
||||
/// Returns a reference to the global profile cache (thread-safe).
|
||||
///
|
||||
/// Initializes the global state if it hasn't been initialized yet.
|
||||
pub fn profiles() -> &'static RwLock<BTreeMap<PublicKey, Option<Metadata>>> {
|
||||
&GLOBAL_STATE.get_or_init(init_global_state).cache_profiles
|
||||
}
|
||||
|
||||
/// Synchronously gets a profile from the cache by public key.
|
||||
///
|
||||
/// Returns default metadata if the profile is not cached.
|
||||
pub fn get_cache_profile(key: &PublicKey) -> Profile {
|
||||
let metadata = if let Some(metadata) = profiles().read_blocking().get(key) {
|
||||
metadata.clone().unwrap_or_default()
|
||||
} else {
|
||||
Metadata::default()
|
||||
};
|
||||
|
||||
Profile::new(*key, metadata)
|
||||
}
|
||||
|
||||
/// Asynchronously gets a profile from the cache by public key.
|
||||
///
|
||||
/// Returns default metadata if the profile isn't cached.
|
||||
pub async fn async_cache_profile(key: &PublicKey) -> Profile {
|
||||
let metadata = if let Some(metadata) = profiles().read().await.get(key) {
|
||||
metadata.clone().unwrap_or_default()
|
||||
} else {
|
||||
Metadata::default()
|
||||
};
|
||||
|
||||
Profile::new(*key, metadata)
|
||||
}
|
||||
|
||||
/// Synchronously inserts or updates a profile in the cache.
|
||||
pub fn insert_cache_profile(key: PublicKey, metadata: Option<Metadata>) {
|
||||
profiles()
|
||||
.write_blocking()
|
||||
.entry(key)
|
||||
.and_modify(|entry| {
|
||||
if entry.is_none() {
|
||||
*entry = metadata.clone();
|
||||
}
|
||||
})
|
||||
.or_insert_with(|| metadata);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user