feat: group metadata query

This commit is contained in:
2024-10-29 15:05:03 +07:00
parent d87371aec4
commit 11dcef4e87
23 changed files with 323 additions and 169 deletions

View File

@@ -32,41 +32,28 @@ pub struct Mention {
#[tauri::command]
#[specta::specta]
pub async fn get_profile(
id: String,
cache_only: bool,
state: State<'_, Nostr>,
) -> Result<String, String> {
pub async fn get_profile(id: String, state: State<'_, Nostr>) -> Result<String, String> {
let client = &state.client;
let public_key = PublicKey::parse(&id).map_err(|e| e.to_string())?;
if cache_only {
let profile = client
.database()
.profile(public_key)
.await
.map_err(|e| e.to_string())?;
return Ok(profile.metadata().as_json());
};
let filter = Filter::new()
.author(public_key)
.kind(Kind::Metadata)
.limit(1);
let mut metadata = Metadata::new();
let mut rx = client
.stream_events(vec![filter], Some(Duration::from_secs(5)))
let events = client
.database()
.query(vec![filter])
.await
.map_err(|e| e.to_string())?;
while let Some(event) = rx.next().await {
metadata = Metadata::from_json(&event.content).map_err(|e| e.to_string())?;
match events.first() {
Some(event) => match Metadata::from_json(&event.content) {
Ok(metadata) => Ok(metadata.as_json()),
Err(e) => Err(e.to_string()),
},
None => Err("Metadata not found".into()),
}
Ok(metadata.as_json())
}
#[tauri::command]
@@ -627,15 +614,15 @@ pub async fn get_notifications(id: String, state: State<'_, Nostr>) -> Result<Ve
#[tauri::command]
#[specta::specta]
pub fn get_user_settings(state: State<'_, Nostr>) -> Result<Settings, String> {
Ok(state.settings.lock().unwrap().clone())
pub async fn get_user_settings(state: State<'_, Nostr>) -> Result<Settings, String> {
Ok(state.settings.lock().await.clone())
}
#[tauri::command]
#[specta::specta]
pub async fn set_user_settings(settings: String, state: State<'_, Nostr>) -> Result<(), String> {
let parsed: Settings = serde_json::from_str(&settings).map_err(|e| e.to_string())?;
state.settings.lock().unwrap().clone_from(&parsed);
state.settings.lock().await.clone_from(&parsed);
Ok(())
}

View File

@@ -12,25 +12,32 @@ use serde::{Deserialize, Serialize};
use specta::Type;
use specta_typescript::Typescript;
use std::{
collections::HashSet,
fs,
io::{self, BufRead},
str::FromStr,
sync::Mutex,
time::Duration,
};
use tauri::{path::BaseDirectory, Emitter, EventTarget, Manager};
use tauri::{path::BaseDirectory, Emitter, EventTarget, Listener, Manager};
use tauri_plugin_decorum::WebviewWindowExt;
use tauri_plugin_notification::{NotificationExt, PermissionState};
use tauri_specta::{collect_commands, Builder};
use tokio::{sync::Mutex, sync::RwLock, time::sleep};
pub mod commands;
pub mod common;
pub struct Nostr {
client: Client,
queue: RwLock<HashSet<PublicKey>>,
settings: Mutex<Settings>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Payload {
id: String,
}
#[derive(Clone, Serialize, Deserialize, Type)]
pub struct Settings {
proxy: Option<String>,
@@ -146,6 +153,7 @@ fn main() {
.setup(move |app| {
let handle = app.handle();
let handle_clone = handle.clone();
let handle_clone_child = handle_clone.clone();
let main_window = app.get_webview_window("main").unwrap();
let config_dir = handle
@@ -174,7 +182,7 @@ 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)))
@@ -229,9 +237,40 @@ fn main() {
// Create global state
app.manage(Nostr {
client,
queue: RwLock::new(HashSet::new()),
settings: Mutex::new(Settings::default()),
});
// Listen for request metadata
app.listen_any("request_metadata", move |event| {
let payload = event.payload();
let parsed_payload: Payload = serde_json::from_str(payload).expect("Parse failed");
let handle = handle_clone_child.clone();
tauri::async_runtime::spawn(async move {
let state = handle.state::<Nostr>();
let client = &state.client;
if let Ok(public_key) = PublicKey::parse(parsed_payload.id) {
let mut write_queue = state.queue.write().await;
write_queue.insert(public_key);
}
sleep(Duration::from_millis(300)).await;
let read_queue = state.queue.read().await;
let authors: Vec<PublicKey> = read_queue.iter().copied().collect();
let filter = Filter::new().authors(authors).kind(Kind::Metadata);
let opts = SubscribeAutoCloseOptions::default()
.filter(FilterOptions::WaitDurationAfterEOSE(Duration::from_secs(3)));
if client.subscribe(vec![filter], Some(opts)).await.is_ok() {
let mut write_queue = state.queue.write().await;
write_queue.clear();
}
});
});
// Run notification thread
tauri::async_runtime::spawn(async move {
let state = handle_clone.state::<Nostr>();
@@ -278,46 +317,52 @@ fn main() {
let _ = client
.handle_notifications(|notification| async {
if let RelayPoolNotification::Event {
event,
subscription_id,
..
} = notification
{
// Handle events from notification subscription
if subscription_id == notification_id {
// Send native notification
if allow_notification {
let author = client
.database()
.profile(event.pubkey)
.await
.unwrap_or_else(|_| {
DatabaseProfile::new(event.pubkey, Metadata::new())
});
#[allow(clippy::collapsible_match)]
if let RelayPoolNotification::Message { message, .. } = notification {
if let RelayMessage::Event {
event,
subscription_id,
..
} = message
{
if subscription_id == notification_id {
// Send native notification
if allow_notification {
let author = client
.database()
.profile(event.pubkey)
.await
.unwrap_or_else(|_| {
DatabaseProfile::new(event.pubkey, Metadata::new())
});
send_event_notification(
&event,
author.metadata(),
&handle_clone,
);
}
} else if event.kind != Kind::RelayList {
let payload = RichEvent {
raw: event.as_json(),
parsed: if event.kind == Kind::TextNote {
Some(parse_event(&event.content).await)
} else {
None
},
};
send_event_notification(
&event,
author.metadata(),
&handle_clone,
);
}
} else if event.kind == Kind::Metadata {
if let Err(e) = handle_clone.emit("metadata", event.as_json()) {
println!("Emitter error: {}", e)
}
} else if event.kind != Kind::RelayList {
let payload = RichEvent {
raw: event.as_json(),
parsed: if event.kind == Kind::TextNote {
Some(parse_event(&event.content).await)
} else {
None
},
};
if let Err(e) = handle_clone.emit_to(
EventTarget::labeled(subscription_id.to_string()),
"event",
payload,
) {
println!("Emitter error: {}", e)
if let Err(e) = handle_clone.emit_to(
EventTarget::labeled(subscription_id.to_string()),
"event",
payload,
) {
println!("Emitter error: {}", e)
}
}
}
}