From 9b02ab58420f72d230eff59f997b2ac23beeecfb Mon Sep 17 00:00:00 2001 From: reya Date: Wed, 30 Oct 2024 16:02:09 +0700 Subject: [PATCH] feat: re-enable gossip and optimize --- src-tauri/resources/relays.txt | 3 +- src-tauri/src/commands/event.rs | 78 ++++++---- src-tauri/src/commands/metadata.rs | 138 ++++++++---------- src-tauri/src/commands/window.rs | 13 +- src-tauri/src/main.rs | 91 +++++++++--- .../columns/_layout/launchpad.$id.lazy.tsx | 12 +- 6 files changed, 201 insertions(+), 134 deletions(-) diff --git a/src-tauri/resources/relays.txt b/src-tauri/resources/relays.txt index bcadd81e..6975d47b 100644 --- a/src-tauri/resources/relays.txt +++ b/src-tauri/resources/relays.txt @@ -1,4 +1,5 @@ wss://relay.damus.io, wss://relay.primal.net, -wss://relay.nostr.band, wss://nostr.fmt.wiz.biz, +wss://directory.yabu.me, +wss://purplepag.es, diff --git a/src-tauri/src/commands/event.rs b/src-tauri/src/commands/event.rs index 9bc665f5..8f35bd0b 100644 --- a/src-tauri/src/commands/event.rs +++ b/src-tauri/src/commands/event.rs @@ -77,13 +77,20 @@ pub async fn get_replies(id: String, state: State<'_, Nostr>) -> Result Ok(process_event(client, events, true).await), - Err(err) => Err(err.to_string()), + .map_err(|e| e.to_string())?; + + while let Some(event) = rx.next().await { + events.insert(event); } + + let alt_events = process_event(client, events, true).await; + + Ok(alt_events) } #[tauri::command] @@ -101,13 +108,20 @@ pub async fn get_all_events_by_author( .author(author) .limit(limit as usize); - match client - .fetch_events(vec![filter], Some(Duration::from_secs(3))) + let mut events = Events::new(&[filter.clone()]); + + let mut rx = client + .stream_events(vec![filter], Some(Duration::from_secs(3))) .await - { - Ok(events) => Ok(process_event(client, events, false).await), - Err(err) => Err(err.to_string()), + .map_err(|e| e.to_string())?; + + while let Some(event) = rx.next().await { + events.insert(event); } + + let alt_events = process_event(client, events, false).await; + + Ok(alt_events) } #[tauri::command] @@ -120,28 +134,35 @@ pub async fn get_all_events_by_authors( let client = &state.client; let as_of = match until { - Some(until) => Timestamp::from_str(&until).map_err(|err| err.to_string())?, + Some(until) => Timestamp::from_str(&until).unwrap_or(Timestamp::now()), None => Timestamp::now(), }; let authors: Vec = public_keys .iter() - .map(|pk| PublicKey::from_str(pk).map_err(|err| err.to_string())) - .collect::, _>>()?; + .filter_map(|pk| PublicKey::from_str(pk).ok()) + .collect(); let filter = Filter::new() + .authors(authors) .kinds(vec![Kind::TextNote, Kind::Repost]) .limit(FETCH_LIMIT) - .until(as_of) - .authors(authors); + .until(as_of); - match client - .fetch_events(vec![filter], Some(Duration::from_secs(3))) + let mut events = Events::new(&[filter.clone()]); + + let mut rx = client + .stream_events(vec![filter], Some(Duration::from_secs(3))) .await - { - Ok(events) => Ok(process_event(client, events, false).await), - Err(err) => Err(err.to_string()), + .map_err(|e| e.to_string())?; + + while let Some(event) = rx.next().await { + events.insert(event); } + + let alt_events = process_event(client, events, false).await; + + Ok(alt_events) } #[tauri::command] @@ -164,13 +185,20 @@ pub async fn get_all_events_by_hashtags( .until(as_of) .hashtags(hashtags); - match client - .fetch_events(vec![filter], Some(Duration::from_secs(3))) + let mut events = Events::new(&[filter.clone()]); + + let mut rx = client + .stream_events(vec![filter], Some(Duration::from_secs(3))) .await - { - Ok(events) => Ok(process_event(client, events, false).await), - Err(err) => Err(err.to_string()), + .map_err(|e| e.to_string())?; + + while let Some(event) = rx.next().await { + events.insert(event); } + + let alt_events = process_event(client, events, false).await; + + Ok(alt_events) } #[tauri::command] diff --git a/src-tauri/src/commands/metadata.rs b/src-tauri/src/commands/metadata.rs index cc296745..5057e619 100644 --- a/src-tauri/src/commands/metadata.rs +++ b/src-tauri/src/commands/metadata.rs @@ -5,10 +5,7 @@ use specta::Type; use std::{str::FromStr, time::Duration}; use tauri::{Emitter, Manager, State}; -use crate::{ - common::{get_latest_event, process_event}, - Nostr, RichEvent, -}; +use crate::{common::process_event, Nostr, RichEvent}; #[derive(Clone, Serialize, Deserialize, Type)] pub struct Mention { @@ -71,35 +68,15 @@ pub async fn get_contact_list(id: String, state: State<'_, Nostr>) -> Result = Vec::new(); - - match client - .fetch_events(vec![filter], Some(Duration::from_secs(3))) + let contact_list = client + .database() + .contacts_public_keys(public_key) .await - { - Ok(events) => { - if let Some(event) = events.into_iter().next() { - for tag in event.tags.into_iter() { - if let Some(TagStandard::PublicKey { - public_key, - uppercase: false, - .. - }) = tag.to_standardized() - { - contact_list.push(public_key.to_hex()) - } - } - } + .map_err(|e| e.to_string())?; - Ok(contact_list) - } - Err(e) => Err(e.to_string()), - } + let pubkeys: Vec = contact_list.into_iter().map(|pk| pk.to_hex()).collect(); + + Ok(pubkeys) } #[tauri::command] @@ -120,21 +97,9 @@ pub async fn is_contact(id: String, state: State<'_, Nostr>) -> Result { - if let Some(event) = events.into_iter().next() { - let pubkeys = event.tags.public_keys().collect::>(); - Ok(pubkeys.iter().any(|&i| i == &public_key)) - } else { - Ok(false) - } - } - Err(e) => Err(e.to_string()), + match client.database().contacts_public_keys(public_key).await { + Ok(public_keys) => Ok(public_keys.iter().any(|i| i == &public_key)), + Err(_) => Ok(false), } } @@ -244,16 +209,15 @@ pub async fn set_group( pub async fn get_group(id: String, state: State<'_, Nostr>) -> Result { let client = &state.client; let event_id = EventId::from_str(&id).map_err(|e| e.to_string())?; - let filter = Filter::new().kind(Kind::FollowSet).id(event_id); - match client - .fetch_events(vec![filter], Some(Duration::from_secs(3))) - .await - { - Ok(events) => match get_latest_event(&events) { - Some(ev) => Ok(ev.as_json()), - None => Err("Not found.".to_string()), - }, + match client.database().event_by_id(&event_id).await { + Ok(event) => { + if let Some(ev) = event { + Ok(ev.as_json()) + } else { + Err("Event not found".into()) + } + } Err(e) => Err(e.to_string()), } } @@ -273,11 +237,17 @@ pub async fn get_all_newsfeeds( .author(public_key) .limit(1); - let remote_events = client - .fetch_events(vec![groups], Some(Duration::from_secs(3))) + let mut remote_events = Events::new(&[groups.clone()]); + + let mut rx = client + .stream_events(vec![groups], Some(Duration::from_secs(3))) .await .map_err(|e| e.to_string())?; + while let Some(event) = rx.next().await { + remote_events.insert(event); + } + let contact_events = client .fetch_events(vec![contacts], Some(Duration::from_secs(3))) .await @@ -349,18 +319,15 @@ pub async fn set_interest( pub async fn get_interest(id: String, state: State<'_, Nostr>) -> Result { let client = &state.client; let event_id = EventId::from_str(&id).map_err(|e| e.to_string())?; - let filter = Filter::new() - .kinds(vec![Kind::Interests, Kind::InterestSet]) - .id(event_id); - match client - .fetch_events(vec![filter], Some(Duration::from_secs(3))) - .await - { - Ok(events) => match get_latest_event(&events) { - Some(ev) => Ok(ev.as_json()), - None => Err("Not found.".to_string()), - }, + match client.database().event_by_id(&event_id).await { + Ok(event) => { + if let Some(ev) = event { + Ok(ev.as_json()) + } else { + Err("Event not found".into()) + } + } Err(e) => Err(e.to_string()), } } @@ -373,17 +340,25 @@ pub async fn get_all_interests( ) -> Result, String> { let client = &state.client; let public_key = PublicKey::parse(&id).map_err(|e| e.to_string())?; + let filter = Filter::new() .kinds(vec![Kind::InterestSet, Kind::Interests]) .author(public_key); - match client - .fetch_events(vec![filter], Some(Duration::from_secs(3))) + let mut events = Events::new(&[filter.clone()]); + + let mut rx = client + .stream_events(vec![filter], Some(Duration::from_secs(3))) .await - { - Ok(events) => Ok(process_event(client, events, false).await), - Err(e) => Err(e.to_string()), + .map_err(|e| e.to_string())?; + + while let Some(event) = rx.next().await { + events.insert(event); } + + let alt_events = process_event(client, events, false).await; + + Ok(alt_events) } #[tauri::command] @@ -570,13 +545,20 @@ pub async fn get_notifications(id: String, state: State<'_, Nostr>) -> Result Ok(events.into_iter().map(|ev| ev.as_json()).collect()), - Err(err) => Err(err.to_string()), + .map_err(|e| e.to_string())?; + + while let Some(event) = rx.next().await { + events.insert(event); } + + let alt_events = events.into_iter().map(|ev| ev.as_json()).collect(); + + Ok(alt_events) } #[tauri::command] diff --git a/src-tauri/src/commands/window.rs b/src-tauri/src/commands/window.rs index dd15b40d..97177013 100644 --- a/src-tauri/src/commands/window.rs +++ b/src-tauri/src/commands/window.rs @@ -82,17 +82,19 @@ pub async fn create_column( let subscription_id = SubscriptionId::new(webview.label()); - let filter = - Filter::new().authors(contact_list).kinds(vec![ + let filter = Filter::new() + .authors(contact_list) + .kinds(vec![ Kind::TextNote, Kind::Repost, Kind::EventDeletion, - ]); + ]) + .since(Timestamp::now()); if let Err(e) = client .subscribe_with_id( subscription_id, - vec![filter.clone().since(Timestamp::now())], + vec![filter], None, ) .await @@ -111,7 +113,8 @@ pub async fn create_column( let filter = Filter::new() .event(event_id) - .kinds(vec![Kind::TextNote, Kind::Custom(1111)]); + .kinds(vec![Kind::TextNote, Kind::Custom(1111)]) + .since(Timestamp::now()); if let Err(e) = client .subscribe_with_id( diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index fd926255..24bd96b5 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -147,6 +147,7 @@ fn main() { let handle = app.handle(); let handle_clone = handle.clone(); let handle_clone_child = handle_clone.clone(); + let handle_clone_event = handle_clone_child.clone(); let main_window = app.get_webview_window("main").unwrap(); let config_dir = handle @@ -175,12 +176,9 @@ fn main() { // Config let opts = Options::new() - .gossip(false) + .gossip(true) .max_avg_latency(Duration::from_millis(300)) .automatic_authentication(true) - .connection_timeout(Some(Duration::from_secs(5))) - .send_timeout(Some(Duration::from_secs(10))) - .wait_for_send(false) .timeout(Duration::from_secs(5)); // Setup nostr client @@ -217,8 +215,6 @@ fn main() { } } - let _ = client.add_discovery_relay("wss://purplepag.es/").await; - let _ = client.add_discovery_relay("wss://directory.yabu.me/").await; let _ = client.add_discovery_relay("wss://user.kindpag.es/").await; // Connect @@ -227,6 +223,62 @@ fn main() { client }); + // Trigger some actions for window events + main_window.on_window_event(move |event| match event { + tauri::WindowEvent::Focused(focused) => { + if !focused { + let handle = handle_clone_event.clone(); + + tauri::async_runtime::spawn(async move { + let state = handle.state::(); + let client = &state.client; + + let filter = Filter::new().kinds(vec![ + Kind::TextNote, + Kind::Repost, + Kind::ContactList, + Kind::FollowSet, + ]); + + // Get all public keys in database + if let Ok(events) = client.database().query(vec![filter]).await { + let public_keys: HashSet = events + .iter() + .flat_map(|ev| ev.tags.public_keys().copied()) + .collect(); + let pk_vec: Vec = public_keys.into_iter().collect(); + + for chunk in pk_vec.chunks(500) { + if chunk.is_empty() { + return; + } + + let authors = chunk.to_owned(); + let filter = Filter::new() + .authors(authors) + .kinds(vec![ + Kind::Metadata, + Kind::TextNote, + Kind::FollowSet, + Kind::Interests, + Kind::InterestSet, + ]) + .limit(2000); + + let opts = SyncOptions::default(); + + if let Err(e) = client.sync(filter, &opts).await { + println!("Sync error: {}", e) + } + } + } + }); + } + } + tauri::WindowEvent::Moved(_size) => {} + _ => {} + }); + // Create global state app.manage(Nostr { client, @@ -276,24 +328,27 @@ fn main() { let accounts = get_all_accounts(); if !accounts.is_empty() { + let subscription_id = SubscriptionId::new(NOTIFICATION_SUB_ID); + let public_keys: Vec = accounts .iter() - .filter_map(|acc| { - if let Ok(pk) = PublicKey::from_str(acc) { - Some(pk) - } else { - None - } - }) + .filter_map(|acc| PublicKey::from_str(acc).ok()) .collect(); + let filter = Filter::new() + .pubkeys(public_keys) + .kinds(vec![ + Kind::TextNote, + Kind::Repost, + Kind::Reaction, + Kind::ZapReceipt, + Kind::Custom(1111), + ]) + .since(Timestamp::now()); + // Subscribe for new notification if let Err(e) = client - .subscribe_with_id( - SubscriptionId::new(NOTIFICATION_SUB_ID), - vec![Filter::new().pubkeys(public_keys).since(Timestamp::now())], - None, - ) + .subscribe_with_id(subscription_id, vec![filter], None) .await { println!("Error: {}", e) diff --git a/src/routes/columns/_layout/launchpad.$id.lazy.tsx b/src/routes/columns/_layout/launchpad.$id.lazy.tsx index 95aaa27f..bc6906e6 100644 --- a/src/routes/columns/_layout/launchpad.$id.lazy.tsx +++ b/src/routes/columns/_layout/launchpad.$id.lazy.tsx @@ -88,13 +88,11 @@ function Newsfeeds() { {item.tags .filter((tag) => tag[0] === "p") .map((tag) => ( -
- - - - - -
+ + + + + ))}