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

@@ -6,7 +6,7 @@ use std::sync::Arc;
use std::time::Duration;
use anyhow::{anyhow, Context as AnyhowContext, Error};
use common::EventUtils;
use common::{EventUtils, BOOTSTRAP_RELAYS};
use device::DeviceRegistry;
use flume::Sender;
use fuzzy_matcher::skim::SkimMatcherV2;
@@ -16,7 +16,7 @@ use gpui::{
};
use nostr_sdk::prelude::*;
use smallvec::{smallvec, SmallVec};
use state::{tracker, NostrRegistry, RelayState, DEVICE_GIFTWRAP, USER_GIFTWRAP};
use state::{tracker, NostrAddress, NostrRegistry, RelayState, DEVICE_GIFTWRAP, USER_GIFTWRAP};
mod message;
mod room;
@@ -336,6 +336,7 @@ impl ChatRegistry {
}
/// Emit an open room event.
///
/// If the room is new, add it to the registry.
pub fn emit_room(&mut self, room: WeakEntity<Room>, cx: &mut Context<Self>) {
if let Some(room) = room.upgrade() {
@@ -364,28 +365,96 @@ impl ChatRegistry {
cx.notify();
}
/// Search rooms by their name.
pub fn search(&self, query: &str, cx: &App) -> Vec<Entity<Room>> {
let matcher = SkimMatcherV2::default();
/// Find for rooms that match the query.
pub fn find(&self, query: &str, cx: &App) -> Task<Result<Vec<Entity<Room>>, Error>> {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let http_client = cx.http_client();
let query = query.to_string();
self.rooms
.iter()
.filter(|room| {
matcher
.fuzzy_match(room.read(cx).display_name(cx).as_ref(), query)
.is_some()
if let Ok(addr) = Nip05Address::parse(&query) {
let task: Task<Result<PublicKey, Error>> = cx.background_spawn(async move {
let profile = addr.profile(&http_client).await?;
let public_key = profile.public_key;
let opts = SubscribeAutoCloseOptions::default()
.exit_policy(ReqExitPolicy::ExitOnEOSE)
.timeout(Some(Duration::from_secs(3)));
// Construct the filter for the metadata event
let filter = Filter::new()
.kind(Kind::Metadata)
.author(public_key)
.limit(1);
// Subscribe to bootstrap relays
client
.subscribe_to(BOOTSTRAP_RELAYS, vec![filter], Some(opts))
.await?;
Ok(public_key)
});
cx.spawn(async move |cx| {
let public_key = task.await?;
let results = cx.read_global::<GlobalChatRegistry, _>(|this, cx| {
this.0
.read_with(cx, |this, cx| this.find_rooms(&public_key.to_hex(), cx))
});
Ok(results)
})
.cloned()
.collect()
} else {
cx.spawn(async move |cx| {
let results = cx.read_global::<GlobalChatRegistry, _>(|this, cx| {
this.0.read_with(cx, |this, cx| this.find_rooms(&query, cx))
});
Ok(results)
})
}
}
/// Search rooms by public keys.
pub fn search_by_public_key(&self, public_key: PublicKey, cx: &App) -> Vec<Entity<Room>> {
self.rooms
.iter()
.filter(|room| room.read(cx).members.contains(&public_key))
.cloned()
.collect()
/// Internal find function for finding rooms based on a query.
fn find_rooms(&self, query: &str, cx: &App) -> Vec<Entity<Room>> {
let matcher = SkimMatcherV2::default();
if let Ok(public_key) = PublicKey::parse(query) {
self.rooms
.iter()
.filter(|room| room.read(cx).members.contains(&public_key))
.cloned()
.collect()
} else {
self.rooms
.iter()
.filter(|room| {
matcher
.fuzzy_match(room.read(cx).display_name(cx).as_ref(), query)
.is_some()
})
.cloned()
.collect()
}
}
/// Construct a chat room based on NIP-05 address.
pub fn address_to_room(&self, addr: Nip05Address, cx: &App) -> Task<Result<Room, Error>> {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let http_client = cx.http_client();
cx.background_spawn(async move {
let signer = client.signer().await?;
let public_key = signer.get_public_key().await?;
// Get the profile belonging to the address
let profile = addr.profile(&http_client).await?;
// Construct the room
let receivers = vec![profile.public_key];
let room = Room::new(None, public_key, receivers);
Ok(room)
})
}
/// Reset the registry.
@@ -531,7 +600,7 @@ impl ChatRegistry {
}
}
/// Parse a Nostr event into a Coop Message and push it to the belonging room
/// Parse a nostr event into a message and push it to the belonging room
///
/// If the room doesn't exist, it will be created.
/// Updates room ordering based on the most recent messages.
@@ -578,7 +647,7 @@ impl ChatRegistry {
}
}
// Unwraps a gift-wrapped event and processes its contents.
/// Unwraps a gift-wrapped event and processes its contents.
async fn extract_rumor(
client: &Client,
device_signer: &Option<Arc<dyn NostrSigner>>,
@@ -602,7 +671,7 @@ impl ChatRegistry {
Ok(rumor_unsigned)
}
// Helper method to try unwrapping with different signers
/// Helper method to try unwrapping with different signers
async fn try_unwrap(
client: &Client,
device_signer: &Option<Arc<dyn NostrSigner>>,