This commit is contained in:
2026-03-10 14:11:28 +07:00
parent 2b2ff135ba
commit 8850f158ab
7 changed files with 71 additions and 97 deletions

View File

@@ -1,5 +1,4 @@
use std::cmp::Ordering;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::time::Duration;
@@ -10,7 +9,7 @@ use itertools::Itertools;
use nostr_sdk::prelude::*;
use person::{Person, PersonRegistry};
use settings::{RoomConfig, SignerKind};
use state::{NostrRegistry, BOOTSTRAP_RELAYS, TIMEOUT};
use state::{NostrRegistry, TIMEOUT};
use crate::NewMessage;
@@ -333,9 +332,6 @@ impl Room {
let signer = nostr.read(cx).signer();
let sender = signer.public_key();
// Get room's id
let id = self.id;
// Get all members, excluding the sender
let members: Vec<PublicKey> = self
.members
@@ -345,30 +341,27 @@ impl Room {
.collect();
cx.background_spawn(async move {
let id = SubscriptionId::new(format!("room-{id}"));
let opts = SubscribeAutoCloseOptions::default()
.exit_policy(ReqExitPolicy::ExitOnEOSE)
.timeout(Some(Duration::from_secs(TIMEOUT)));
// Construct filters for each member
let filters: Vec<Filter> = members
.into_iter()
.map(|public_key| {
Filter::new()
.author(public_key)
.kind(Kind::RelayList)
.limit(1)
})
.collect();
for public_key in members.into_iter() {
let inbox = Filter::new()
.author(public_key)
.kind(Kind::InboxRelays)
.limit(1);
// Construct target for subscription
let target: HashMap<&str, Vec<Filter>> = BOOTSTRAP_RELAYS
.into_iter()
.map(|relay| (relay, filters.clone()))
.collect();
let announcement = Filter::new()
.author(public_key)
.kind(Kind::Custom(10044))
.limit(1);
// Subscribe to the target
client.subscribe(target).close_on(opts).with_id(id).await?;
// Subscribe to the target
client
.subscribe(vec![inbox, announcement])
.close_on(opts)
.await?;
}
Ok(())
})
@@ -491,15 +484,9 @@ impl Room {
// Process each member
for member in members {
let relays = member.messaging_relays();
let announcement = member.announcement();
let public_key = member.public_key();
if relays.is_empty() {
reports.push(SendReport::new(public_key).error("No messaging relays"));
continue;
}
// Handle encryption signer requirements
if signer_kind.encryption() {
if announcement.is_none() {
@@ -535,8 +522,7 @@ impl Room {
SignerKind::User => (member.public_key(), user_signer.clone()),
};
match send_gift_wrap(&client, &signer, &receiver, &rumor, relays, public_key).await
{
match send_gift_wrap(&client, &signer, &receiver, &rumor, public_key).await {
Ok((report, _)) => {
reports.push(report);
sents += 1;
@@ -549,12 +535,10 @@ impl Room {
// Send backup to current user if needed
if backup && sents >= 1 {
let relays = sender.messaging_relays();
let public_key = sender.public_key();
let signer = encryption_signer.as_ref().unwrap_or(&user_signer);
match send_gift_wrap(&client, signer, &public_key, &rumor, relays, public_key).await
{
match send_gift_wrap(&client, signer, &public_key, &rumor, public_key).await {
Ok((report, _)) => reports.push(report),
Err(report) => reports.push(report),
}
@@ -571,22 +555,16 @@ async fn send_gift_wrap<T>(
signer: &T,
receiver: &PublicKey,
rumor: &UnsignedEvent,
relays: &[RelayUrl],
public_key: PublicKey,
) -> Result<(SendReport, bool), SendReport>
where
T: NostrSigner + 'static,
{
// Ensure relay connections
for url in relays {
client.add_relay(url).and_connect().await.ok();
}
match EventBuilder::gift_wrap(signer, receiver, rumor.clone(), []).await {
Ok(event) => {
match client
.send_event(&event)
.to(relays)
.to_nip17()
.ack_policy(AckPolicy::none())
.await
{

View File

@@ -2,9 +2,9 @@ use std::sync::{Arc, Mutex};
use assets::Assets;
use gpui::{
actions, point, px, size, App, AppContext, Bounds, KeyBinding, Menu, MenuItem, SharedString,
TitlebarOptions, WindowBackgroundAppearance, WindowBounds, WindowDecorations, WindowKind,
WindowOptions,
App, AppContext, Bounds, KeyBinding, Menu, MenuItem, SharedString, TitlebarOptions,
WindowBackgroundAppearance, WindowBounds, WindowDecorations, WindowKind, WindowOptions,
actions, point, px, size,
};
use gpui_platform::application;
use state::{APP_ID, CLIENT_NAME};
@@ -86,7 +86,7 @@ fn main() {
state::init(window, cx);
// Initialize person registry
person::init(cx);
person::init(window, cx);
// Initialize relay auth registry
relay_auth::init(window, cx);

View File

@@ -5,7 +5,7 @@ use std::time::Duration;
use anyhow::{Error, anyhow};
use common::EventUtils;
use gpui::{App, AppContext, Context, Entity, Global, Task};
use gpui::{App, AppContext, Context, Entity, Global, Task, Window};
use nostr_sdk::prelude::*;
use smallvec::{SmallVec, smallvec};
use state::{Announcement, BOOTSTRAP_RELAYS, NostrRegistry, TIMEOUT};
@@ -14,8 +14,8 @@ mod person;
pub use person::*;
pub fn init(cx: &mut App) {
PersonRegistry::set_global(cx.new(PersonRegistry::new), cx);
pub fn init(window: &mut Window, cx: &mut App) {
PersonRegistry::set_global(cx.new(|cx| PersonRegistry::new(window, cx)), cx);
}
struct GlobalPersonRegistry(Entity<PersonRegistry>);
@@ -42,7 +42,7 @@ pub struct PersonRegistry {
sender: flume::Sender<PublicKey>,
/// Tasks for asynchronous operations
_tasks: SmallVec<[Task<()>; 4]>,
tasks: SmallVec<[Task<()>; 4]>,
}
impl PersonRegistry {
@@ -57,7 +57,7 @@ impl PersonRegistry {
}
/// Create a new person registry instance
fn new(cx: &mut Context<Self>) -> Self {
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
@@ -111,33 +111,16 @@ impl PersonRegistry {
}),
);
tasks.push(
// Load all user profiles from the database
cx.spawn(async move |this, cx| {
let result = cx
.background_executor()
.await_on_background(async move { load_persons(&client).await })
.await;
match result {
Ok(persons) => {
this.update(cx, |this, cx| {
this.bulk_inserts(persons, cx);
})
.ok();
}
Err(e) => {
log::error!("Failed to load all persons from the database: {e}");
}
};
}),
);
// Load all user profiles from the database
cx.defer_in(window, |this, _window, cx| {
this.load(cx);
});
Self {
persons: HashMap::new(),
seens: Rc::new(RefCell::new(HashSet::new())),
sender: mta_tx,
_tasks: tasks,
tasks,
}
}
@@ -163,25 +146,21 @@ impl PersonRegistry {
let metadata = Metadata::from_json(&event.content).unwrap_or_default();
let person = Person::new(event.pubkey, metadata);
let val = Box::new(person);
// Send
tx.send_async(Dispatch::Person(val)).await.ok();
}
Kind::ContactList => {
let public_keys = event.extract_public_keys();
// Get metadata for all public keys
get_metadata(client, public_keys).await.ok();
}
Kind::InboxRelays => {
let val = Box::new(event.into_owned());
// Send
tx.send_async(Dispatch::Relays(val)).await.ok();
}
Kind::Custom(10044) => {
let val = Box::new(event.into_owned());
// Send
tx.send_async(Dispatch::Announcement(val)).await.ok();
}
@@ -198,7 +177,7 @@ impl PersonRegistry {
loop {
match flume::Selector::new()
.recv(rx, |result| result.ok())
.wait_timeout(Duration::from_secs(2))
.wait_timeout(Duration::from_secs(TIMEOUT))
{
Ok(Some(public_key)) => {
batch.insert(public_key);
@@ -216,6 +195,35 @@ impl PersonRegistry {
}
}
/// Load all user profiles from the database
fn load(&mut self, cx: &mut Context<Self>) {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let task: Task<Result<Vec<Person>, Error>> = cx.background_spawn(async move {
let filter = Filter::new().kind(Kind::Metadata).limit(200);
let events = client.database().query(filter).await?;
let persons = events
.into_iter()
.map(|event| {
let metadata = Metadata::from_json(event.content).unwrap_or_default();
Person::new(event.pubkey, metadata)
})
.collect();
Ok(persons)
});
self.tasks.push(cx.spawn(async move |this, cx| {
if let Ok(persons) = task.await {
this.update(cx, |this, cx| {
this.bulk_inserts(persons, cx);
})
.ok();
}
}));
}
/// Set profile encryption keys announcement
fn set_announcement(&mut self, event: &Event, cx: &mut App) {
let announcement = Announcement::from(event);
@@ -334,19 +342,3 @@ where
Ok(())
}
/// Load all user profiles from the database
async fn load_persons(client: &Client) -> Result<Vec<Person>, Error> {
let filter = Filter::new().kind(Kind::Metadata).limit(200);
let events = client.database().query(filter).await?;
let mut persons = vec![];
for event in events.into_iter() {
let metadata = Metadata::from_json(event.content).unwrap_or_default();
let person = Person::new(event.pubkey, metadata);
persons.push(person);
}
Ok(persons)
}

View File

@@ -283,7 +283,10 @@ impl RelayAuth {
});
window.push_notification(
Notification::success(format!("{} has been authenticated", url)),
Notification::success(format!(
"{} has been authenticated",
url.domain().unwrap_or_default()
)),
cx,
);
}

View File

@@ -40,7 +40,8 @@ pub const WOT_RELAYS: [&str; 1] = ["wss://relay.vertexlab.io"];
pub const SEARCH_RELAYS: [&str; 2] = ["wss://antiprimal.net", "wss://search.nos.today"];
/// Default bootstrap relays
pub const BOOTSTRAP_RELAYS: [&str; 3] = [
pub const BOOTSTRAP_RELAYS: [&str; 4] = [
"wss://relay.damus.io",
"wss://relay.primal.net",
"wss://indexer.coracle.social",
"wss://user.kindpag.es",

View File

@@ -185,7 +185,7 @@ impl NostrRegistry {
}
// Connect to all added relays
client.connect().and_wait(Duration::from_secs(2)).await;
client.connect().await;
Ok(())
});

View File

@@ -405,12 +405,12 @@ impl Render for Notification {
}
}
} else {
let opacity = delta;
let y_offset = match placement {
placement if placement.is_top() => px(-45.) + delta * px(45.),
placement if placement.is_bottom() => px(45.) - delta * px(45.),
_ => px(0.),
};
let opacity = delta;
this.top(px(0.) + y_offset)
.opacity(opacity)
.when(opacity < 0.85, |this| this.shadow_none())