wip: command bar
Some checks failed
Rust / build (ubuntu-latest, stable) (push) Failing after 1m35s
Rust / build (ubuntu-latest, stable) (pull_request) Failing after 1m36s

This commit is contained in:
2026-02-02 15:23:02 +07:00
parent 9b03f873c2
commit f164e86967
19 changed files with 627 additions and 518 deletions

View File

@@ -12,7 +12,9 @@ nostr-lmdb.workspace = true
nostr-connect.workspace = true
gpui.workspace = true
gpui_tokio.workspace = true
smol.workspace = true
reqwest.workspace = true
flume.workspace = true
log.workspace = true
anyhow.workspace = true

View File

@@ -13,20 +13,22 @@ mod device;
mod event;
mod gossip;
mod identity;
mod nip05;
pub use device::*;
pub use event::*;
pub use gossip::*;
pub use identity::*;
pub use nip05::*;
use crate::identity::Identity;
pub fn init(cx: &mut App) {
NostrRegistry::set_global(cx.new(NostrRegistry::new), cx);
}
/// Default timeout for subscription
pub const TIMEOUT: u64 = 3;
/// Default delay for searching
pub const FIND_DELAY: u64 = 600;
/// Default limit for searching
pub const FIND_LIMIT: usize = 20;
/// Default timeout for Nostr Connect
pub const NOSTR_CONNECT_TIMEOUT: u64 = 200;
/// Default Nostr Connect relay
@@ -36,6 +38,13 @@ pub const DEVICE_GIFTWRAP: &str = "device-gift-wraps";
/// Default subscription id for user gift wrap events
pub const USER_GIFTWRAP: &str = "user-gift-wraps";
pub fn init(cx: &mut App) {
// Initialize the tokio runtime
gpui_tokio::init(cx);
NostrRegistry::set_global(cx.new(NostrRegistry::new), cx);
}
struct GlobalNostrRegistry(Entity<NostrRegistry>);
impl Global for GlobalNostrRegistry {}
@@ -796,6 +805,40 @@ impl NostrRegistry {
(signer, uri)
}
/// Perform a NIP-50 global search for user profiles based on a given query
pub fn search(&self, query: &str, cx: &App) -> Task<Result<Vec<Event>, Error>> {
let client = self.client();
let query = query.to_string();
cx.background_spawn(async move {
let mut results: Vec<Event> = Vec::with_capacity(FIND_LIMIT);
// Construct the filter for the search query
let filter = Filter::new()
.search(query.to_lowercase())
.kind(Kind::Metadata)
.limit(FIND_LIMIT);
// Stream events from the search relays
let mut stream = client
.stream_events_from(SEARCH_RELAYS, vec![filter], Duration::from_secs(3))
.await?;
// Collect the results
while let Some((_url, res)) = stream.next().await {
if let Ok(event) = res {
results.push(event);
}
}
if results.is_empty() {
return Err(anyhow!("No results for query {query}"));
}
Ok(results)
})
}
}
#[derive(Debug, Clone)]

60
crates/state/src/nip05.rs Normal file
View File

@@ -0,0 +1,60 @@
use std::sync::Arc;
use anyhow::Error;
use gpui::http_client::{AsyncBody, HttpClient};
use nostr_sdk::prelude::*;
use smol::io::AsyncReadExt;
#[allow(async_fn_in_trait)]
pub trait NostrAddress {
/// Get the NIP-05 profile
async fn profile(&self, client: &Arc<dyn HttpClient>) -> Result<Nip05Profile, Error>;
/// Verify the NIP-05 address
async fn verify(
&self,
client: &Arc<dyn HttpClient>,
public_key: &PublicKey,
) -> Result<bool, Error>;
}
impl NostrAddress for Nip05Address {
async fn profile(&self, client: &Arc<dyn HttpClient>) -> Result<Nip05Profile, Error> {
let mut body = Vec::new();
let mut res = client
.get(self.url().as_str(), AsyncBody::default(), false)
.await?;
// Read the response body into a vector
res.body_mut().read_to_end(&mut body).await?;
// Parse the JSON response
let json: Value = serde_json::from_slice(&body)?;
let profile = Nip05Profile::from_json(self, &json)?;
Ok(profile)
}
async fn verify(
&self,
client: &Arc<dyn HttpClient>,
public_key: &PublicKey,
) -> Result<bool, Error> {
let mut body = Vec::new();
let mut res = client
.get(self.url().as_str(), AsyncBody::default(), false)
.await?;
// Read the response body into a vector
res.body_mut().read_to_end(&mut body).await?;
// Parse the JSON response
let json: Value = serde_json::from_slice(&body)?;
// Verify the NIP-05 address
let verified = nip05::verify_from_json(public_key, self, &json);
Ok(verified)
}
}