feat: improve performance
This commit is contained in:
@@ -2,7 +2,7 @@ use keyring::Entry;
|
||||
use keyring_search::{Limit, List, Search};
|
||||
use nostr_sdk::prelude::*;
|
||||
use serde::Serialize;
|
||||
use std::{collections::HashSet, time::Duration};
|
||||
use std::{collections::HashSet, str::FromStr, time::Duration};
|
||||
use tauri::{Emitter, Manager, State};
|
||||
|
||||
use crate::{Nostr, BOOTSTRAP_RELAYS};
|
||||
@@ -252,6 +252,16 @@ pub async fn login(
|
||||
let _ = client.connect_relay(url).await;
|
||||
}
|
||||
|
||||
// Workaround for https://github.com/rust-nostr/nostr/issues/509
|
||||
// TODO: remove this
|
||||
let _ = client
|
||||
.get_events_from(
|
||||
urls.clone(),
|
||||
vec![Filter::new().kind(Kind::TextNote).limit(0)],
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
let mut inbox_relays = state.inbox_relays.lock().await;
|
||||
inbox_relays.insert(public_key, urls);
|
||||
} else {
|
||||
@@ -275,6 +285,41 @@ pub async fn login(
|
||||
let state = handle.state::<Nostr>();
|
||||
let client = &state.client;
|
||||
|
||||
let filter = Filter::new().kind(Kind::GiftWrap).pubkey(public_key);
|
||||
|
||||
if let Ok(events) = client
|
||||
.get_events_of_with_opts(
|
||||
vec![filter],
|
||||
Some(Duration::from_secs(20)),
|
||||
FilterOptions::WaitDurationAfterEOSE(Duration::from_secs(20)),
|
||||
)
|
||||
.await
|
||||
{
|
||||
// Use fake sig, it doesn't matter.
|
||||
// TODO: Find better way to save unsigned event to database.
|
||||
let fake_sig = Signature::from_str("f9e79d141c004977192d05a86f81ec7c585179c371f7350a5412d33575a2a356433f58e405c2296ed273e2fe0aafa25b641e39cc4e1f3f261ebf55bce0cbac83").unwrap();
|
||||
|
||||
for event in events.iter() {
|
||||
if let Ok(UnwrappedGift { rumor, .. }) = client.unwrap_gift_wrap(event).await {
|
||||
let rumor_clone = rumor.clone();
|
||||
let ev = Event::new(
|
||||
rumor_clone.id.unwrap(),
|
||||
rumor_clone.pubkey,
|
||||
rumor_clone.created_at,
|
||||
rumor_clone.kind,
|
||||
rumor_clone.tags,
|
||||
rumor_clone.content,
|
||||
fake_sig,
|
||||
);
|
||||
|
||||
if let Err(e) = client.database().save_event(&ev).await {
|
||||
println!("Error: {}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
handle.emit("synchronized", ()).unwrap();
|
||||
}
|
||||
|
||||
client
|
||||
.handle_notifications(|notification| async {
|
||||
if let RelayPoolNotification::Event { event, subscription_id, .. } = notification {
|
||||
|
||||
@@ -1,85 +1,57 @@
|
||||
use itertools::Itertools;
|
||||
use nostr_sdk::prelude::*;
|
||||
use serde::Serialize;
|
||||
use std::cmp::Reverse;
|
||||
use std::time::Duration;
|
||||
use tauri::{Emitter, Manager, State};
|
||||
use tauri::State;
|
||||
|
||||
use crate::{
|
||||
common::{process_chat_event, process_message_event},
|
||||
Nostr,
|
||||
};
|
||||
|
||||
#[derive(Clone, Serialize)]
|
||||
pub struct ChatPayload {
|
||||
events: Vec<String>,
|
||||
}
|
||||
use crate::Nostr;
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_chats(
|
||||
state: State<'_, Nostr>,
|
||||
handle: tauri::AppHandle,
|
||||
) -> Result<Vec<String>, String> {
|
||||
pub async fn get_chats(state: State<'_, Nostr>) -> Result<Vec<String>, String> {
|
||||
let client = &state.client;
|
||||
let database = client.database();
|
||||
let signer = client.signer().await.map_err(|e| e.to_string())?;
|
||||
let public_key = signer.public_key().await.map_err(|e| e.to_string())?;
|
||||
|
||||
let filter = Filter::new().kind(Kind::GiftWrap).pubkey(public_key);
|
||||
let filter = Filter::new().kind(Kind::PrivateDirectMessage).pubkey(public_key);
|
||||
|
||||
let events = match database.query(vec![filter.clone()], Order::Desc).await {
|
||||
Ok(events) => process_chat_event(client, events).await,
|
||||
Err(e) => return Err(e.to_string()),
|
||||
};
|
||||
match client.database().query(vec![filter.clone()], Order::Desc).await {
|
||||
Ok(events) => {
|
||||
let ev = events
|
||||
.into_iter()
|
||||
.sorted_by_key(|ev| Reverse(ev.created_at))
|
||||
.filter(|ev| ev.pubkey != public_key)
|
||||
.unique_by(|ev| ev.pubkey)
|
||||
.map(|ev| ev.as_json())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let state = handle.state::<Nostr>();
|
||||
let client = &state.client;
|
||||
|
||||
if let Ok(events) = client.get_events_of(vec![filter], None).await {
|
||||
let rumors = process_chat_event(client, events).await;
|
||||
handle.emit("sync_chat", ChatPayload { events: rumors }).unwrap();
|
||||
Ok(ev)
|
||||
}
|
||||
});
|
||||
|
||||
Ok(events)
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_chat_messages(
|
||||
id: String,
|
||||
state: State<'_, Nostr>,
|
||||
handle: tauri::AppHandle,
|
||||
) -> Result<Vec<String>, String> {
|
||||
pub async fn get_chat_messages(id: String, state: State<'_, Nostr>) -> Result<Vec<String>, String> {
|
||||
let client = &state.client;
|
||||
let database = client.database();
|
||||
|
||||
let signer = client.signer().await.map_err(|e| e.to_string())?;
|
||||
|
||||
let public_key = signer.public_key().await.map_err(|e| e.to_string())?;
|
||||
let receiver = signer.public_key().await.map_err(|e| e.to_string())?;
|
||||
let sender = PublicKey::parse(id.clone()).map_err(|e| e.to_string())?;
|
||||
|
||||
let group = vec![public_key, sender];
|
||||
let filter = Filter::new().kind(Kind::GiftWrap).pubkey(public_key);
|
||||
let recv_filter =
|
||||
Filter::new().kind(Kind::PrivateDirectMessage).author(sender).pubkey(receiver);
|
||||
let sender_filter =
|
||||
Filter::new().kind(Kind::PrivateDirectMessage).author(receiver).pubkey(sender);
|
||||
|
||||
let rumors = match database.query(vec![filter.clone()], Order::Desc).await {
|
||||
Ok(events) => process_message_event(client, events, &group).await,
|
||||
Err(e) => return Err(e.to_string()),
|
||||
};
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let state = handle.state::<Nostr>();
|
||||
let client = &state.client;
|
||||
|
||||
if let Ok(events) = client.get_events_of(vec![filter], None).await {
|
||||
let rumors = process_message_event(client, events, &group).await;
|
||||
let emit_to = format!("sync_chat_{}", id);
|
||||
|
||||
handle.emit(&emit_to, ChatPayload { events: rumors }).unwrap();
|
||||
match client.database().query(vec![recv_filter, sender_filter], Order::Desc).await {
|
||||
Ok(events) => {
|
||||
let ev = events.into_iter().map(|ev| ev.as_json()).collect::<Vec<_>>();
|
||||
Ok(ev)
|
||||
}
|
||||
});
|
||||
|
||||
Ok(rumors)
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
use std::cmp::Reverse;
|
||||
|
||||
use futures::stream::{self, StreamExt};
|
||||
use itertools::Itertools;
|
||||
use nostr_sdk::prelude::*;
|
||||
|
||||
pub async fn process_chat_event(client: &Client, events: Vec<Event>) -> Vec<String> {
|
||||
let rumors = stream::iter(events)
|
||||
.filter_map(|ev| async move {
|
||||
if let Ok(UnwrappedGift { rumor, .. }) = client.unwrap_gift_wrap(&ev).await {
|
||||
if rumor.kind == Kind::PrivateDirectMessage {
|
||||
Some(rumor)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.await;
|
||||
|
||||
let signer = client.signer().await.unwrap();
|
||||
let public_key = signer.public_key().await.unwrap();
|
||||
|
||||
rumors
|
||||
.into_iter()
|
||||
.sorted_by_key(|ev| Reverse(ev.created_at))
|
||||
.filter(|ev| ev.pubkey != public_key)
|
||||
.unique_by(|ev| ev.pubkey)
|
||||
.map(|ev| ev.as_json())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub async fn process_message_event(
|
||||
client: &Client,
|
||||
events: Vec<Event>,
|
||||
group: &Vec<PublicKey>,
|
||||
) -> Vec<String> {
|
||||
stream::iter(events)
|
||||
.filter_map(|ev| async move {
|
||||
if let Ok(UnwrappedGift { rumor, sender }) = client.unwrap_gift_wrap(&ev).await {
|
||||
if group.contains(&sender) && is_member(group, &rumor.tags) {
|
||||
Some(rumor.as_json())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn is_member(group: &Vec<PublicKey>, tags: &Vec<Tag>) -> bool {
|
||||
for tag in tags {
|
||||
if let Some(TagStandard::PublicKey { public_key, .. }) = tag.as_standardized() {
|
||||
if group.contains(public_key) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
@@ -12,7 +12,6 @@ use tauri_plugin_decorum::WebviewWindowExt;
|
||||
use commands::{account::*, chat::*};
|
||||
|
||||
mod commands;
|
||||
mod common;
|
||||
|
||||
pub struct Nostr {
|
||||
client: Client,
|
||||
|
||||
Reference in New Issue
Block a user