.
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@@ -10,7 +9,7 @@ use itertools::Itertools;
|
|||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use person::{Person, PersonRegistry};
|
use person::{Person, PersonRegistry};
|
||||||
use settings::{RoomConfig, SignerKind};
|
use settings::{RoomConfig, SignerKind};
|
||||||
use state::{NostrRegistry, BOOTSTRAP_RELAYS, TIMEOUT};
|
use state::{NostrRegistry, TIMEOUT};
|
||||||
|
|
||||||
use crate::NewMessage;
|
use crate::NewMessage;
|
||||||
|
|
||||||
@@ -333,9 +332,6 @@ impl Room {
|
|||||||
let signer = nostr.read(cx).signer();
|
let signer = nostr.read(cx).signer();
|
||||||
let sender = signer.public_key();
|
let sender = signer.public_key();
|
||||||
|
|
||||||
// Get room's id
|
|
||||||
let id = self.id;
|
|
||||||
|
|
||||||
// Get all members, excluding the sender
|
// Get all members, excluding the sender
|
||||||
let members: Vec<PublicKey> = self
|
let members: Vec<PublicKey> = self
|
||||||
.members
|
.members
|
||||||
@@ -345,30 +341,27 @@ impl Room {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
cx.background_spawn(async move {
|
cx.background_spawn(async move {
|
||||||
let id = SubscriptionId::new(format!("room-{id}"));
|
|
||||||
let opts = SubscribeAutoCloseOptions::default()
|
let opts = SubscribeAutoCloseOptions::default()
|
||||||
.exit_policy(ReqExitPolicy::ExitOnEOSE)
|
.exit_policy(ReqExitPolicy::ExitOnEOSE)
|
||||||
.timeout(Some(Duration::from_secs(TIMEOUT)));
|
.timeout(Some(Duration::from_secs(TIMEOUT)));
|
||||||
|
|
||||||
// Construct filters for each member
|
for public_key in members.into_iter() {
|
||||||
let filters: Vec<Filter> = members
|
let inbox = Filter::new()
|
||||||
.into_iter()
|
.author(public_key)
|
||||||
.map(|public_key| {
|
.kind(Kind::InboxRelays)
|
||||||
Filter::new()
|
.limit(1);
|
||||||
.author(public_key)
|
|
||||||
.kind(Kind::RelayList)
|
|
||||||
.limit(1)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Construct target for subscription
|
let announcement = Filter::new()
|
||||||
let target: HashMap<&str, Vec<Filter>> = BOOTSTRAP_RELAYS
|
.author(public_key)
|
||||||
.into_iter()
|
.kind(Kind::Custom(10044))
|
||||||
.map(|relay| (relay, filters.clone()))
|
.limit(1);
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Subscribe to the target
|
// Subscribe to the target
|
||||||
client.subscribe(target).close_on(opts).with_id(id).await?;
|
client
|
||||||
|
.subscribe(vec![inbox, announcement])
|
||||||
|
.close_on(opts)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@@ -491,15 +484,9 @@ impl Room {
|
|||||||
|
|
||||||
// Process each member
|
// Process each member
|
||||||
for member in members {
|
for member in members {
|
||||||
let relays = member.messaging_relays();
|
|
||||||
let announcement = member.announcement();
|
let announcement = member.announcement();
|
||||||
let public_key = member.public_key();
|
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
|
// Handle encryption signer requirements
|
||||||
if signer_kind.encryption() {
|
if signer_kind.encryption() {
|
||||||
if announcement.is_none() {
|
if announcement.is_none() {
|
||||||
@@ -535,8 +522,7 @@ impl Room {
|
|||||||
SignerKind::User => (member.public_key(), user_signer.clone()),
|
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, _)) => {
|
Ok((report, _)) => {
|
||||||
reports.push(report);
|
reports.push(report);
|
||||||
sents += 1;
|
sents += 1;
|
||||||
@@ -549,12 +535,10 @@ impl Room {
|
|||||||
|
|
||||||
// Send backup to current user if needed
|
// Send backup to current user if needed
|
||||||
if backup && sents >= 1 {
|
if backup && sents >= 1 {
|
||||||
let relays = sender.messaging_relays();
|
|
||||||
let public_key = sender.public_key();
|
let public_key = sender.public_key();
|
||||||
let signer = encryption_signer.as_ref().unwrap_or(&user_signer);
|
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),
|
Ok((report, _)) => reports.push(report),
|
||||||
Err(report) => reports.push(report),
|
Err(report) => reports.push(report),
|
||||||
}
|
}
|
||||||
@@ -571,22 +555,16 @@ async fn send_gift_wrap<T>(
|
|||||||
signer: &T,
|
signer: &T,
|
||||||
receiver: &PublicKey,
|
receiver: &PublicKey,
|
||||||
rumor: &UnsignedEvent,
|
rumor: &UnsignedEvent,
|
||||||
relays: &[RelayUrl],
|
|
||||||
public_key: PublicKey,
|
public_key: PublicKey,
|
||||||
) -> Result<(SendReport, bool), SendReport>
|
) -> Result<(SendReport, bool), SendReport>
|
||||||
where
|
where
|
||||||
T: NostrSigner + 'static,
|
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 {
|
match EventBuilder::gift_wrap(signer, receiver, rumor.clone(), []).await {
|
||||||
Ok(event) => {
|
Ok(event) => {
|
||||||
match client
|
match client
|
||||||
.send_event(&event)
|
.send_event(&event)
|
||||||
.to(relays)
|
.to_nip17()
|
||||||
.ack_policy(AckPolicy::none())
|
.ack_policy(AckPolicy::none())
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ use std::sync::{Arc, Mutex};
|
|||||||
|
|
||||||
use assets::Assets;
|
use assets::Assets;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, point, px, size, App, AppContext, Bounds, KeyBinding, Menu, MenuItem, SharedString,
|
App, AppContext, Bounds, KeyBinding, Menu, MenuItem, SharedString, TitlebarOptions,
|
||||||
TitlebarOptions, WindowBackgroundAppearance, WindowBounds, WindowDecorations, WindowKind,
|
WindowBackgroundAppearance, WindowBounds, WindowDecorations, WindowKind, WindowOptions,
|
||||||
WindowOptions,
|
actions, point, px, size,
|
||||||
};
|
};
|
||||||
use gpui_platform::application;
|
use gpui_platform::application;
|
||||||
use state::{APP_ID, CLIENT_NAME};
|
use state::{APP_ID, CLIENT_NAME};
|
||||||
@@ -86,7 +86,7 @@ fn main() {
|
|||||||
state::init(window, cx);
|
state::init(window, cx);
|
||||||
|
|
||||||
// Initialize person registry
|
// Initialize person registry
|
||||||
person::init(cx);
|
person::init(window, cx);
|
||||||
|
|
||||||
// Initialize relay auth registry
|
// Initialize relay auth registry
|
||||||
relay_auth::init(window, cx);
|
relay_auth::init(window, cx);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use anyhow::{Error, anyhow};
|
use anyhow::{Error, anyhow};
|
||||||
use common::EventUtils;
|
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 nostr_sdk::prelude::*;
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
use state::{Announcement, BOOTSTRAP_RELAYS, NostrRegistry, TIMEOUT};
|
use state::{Announcement, BOOTSTRAP_RELAYS, NostrRegistry, TIMEOUT};
|
||||||
@@ -14,8 +14,8 @@ mod person;
|
|||||||
|
|
||||||
pub use person::*;
|
pub use person::*;
|
||||||
|
|
||||||
pub fn init(cx: &mut App) {
|
pub fn init(window: &mut Window, cx: &mut App) {
|
||||||
PersonRegistry::set_global(cx.new(PersonRegistry::new), cx);
|
PersonRegistry::set_global(cx.new(|cx| PersonRegistry::new(window, cx)), cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GlobalPersonRegistry(Entity<PersonRegistry>);
|
struct GlobalPersonRegistry(Entity<PersonRegistry>);
|
||||||
@@ -42,7 +42,7 @@ pub struct PersonRegistry {
|
|||||||
sender: flume::Sender<PublicKey>,
|
sender: flume::Sender<PublicKey>,
|
||||||
|
|
||||||
/// Tasks for asynchronous operations
|
/// Tasks for asynchronous operations
|
||||||
_tasks: SmallVec<[Task<()>; 4]>,
|
tasks: SmallVec<[Task<()>; 4]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PersonRegistry {
|
impl PersonRegistry {
|
||||||
@@ -57,7 +57,7 @@ impl PersonRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new person registry instance
|
/// 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 nostr = NostrRegistry::global(cx);
|
||||||
let client = nostr.read(cx).client();
|
let client = nostr.read(cx).client();
|
||||||
|
|
||||||
@@ -111,33 +111,16 @@ impl PersonRegistry {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
tasks.push(
|
// Load all user profiles from the database
|
||||||
// Load all user profiles from the database
|
cx.defer_in(window, |this, _window, cx| {
|
||||||
cx.spawn(async move |this, cx| {
|
this.load(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}");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
persons: HashMap::new(),
|
persons: HashMap::new(),
|
||||||
seens: Rc::new(RefCell::new(HashSet::new())),
|
seens: Rc::new(RefCell::new(HashSet::new())),
|
||||||
sender: mta_tx,
|
sender: mta_tx,
|
||||||
_tasks: tasks,
|
tasks,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,25 +146,21 @@ impl PersonRegistry {
|
|||||||
let metadata = Metadata::from_json(&event.content).unwrap_or_default();
|
let metadata = Metadata::from_json(&event.content).unwrap_or_default();
|
||||||
let person = Person::new(event.pubkey, metadata);
|
let person = Person::new(event.pubkey, metadata);
|
||||||
let val = Box::new(person);
|
let val = Box::new(person);
|
||||||
|
|
||||||
// Send
|
// Send
|
||||||
tx.send_async(Dispatch::Person(val)).await.ok();
|
tx.send_async(Dispatch::Person(val)).await.ok();
|
||||||
}
|
}
|
||||||
Kind::ContactList => {
|
Kind::ContactList => {
|
||||||
let public_keys = event.extract_public_keys();
|
let public_keys = event.extract_public_keys();
|
||||||
|
|
||||||
// Get metadata for all public keys
|
// Get metadata for all public keys
|
||||||
get_metadata(client, public_keys).await.ok();
|
get_metadata(client, public_keys).await.ok();
|
||||||
}
|
}
|
||||||
Kind::InboxRelays => {
|
Kind::InboxRelays => {
|
||||||
let val = Box::new(event.into_owned());
|
let val = Box::new(event.into_owned());
|
||||||
|
|
||||||
// Send
|
// Send
|
||||||
tx.send_async(Dispatch::Relays(val)).await.ok();
|
tx.send_async(Dispatch::Relays(val)).await.ok();
|
||||||
}
|
}
|
||||||
Kind::Custom(10044) => {
|
Kind::Custom(10044) => {
|
||||||
let val = Box::new(event.into_owned());
|
let val = Box::new(event.into_owned());
|
||||||
|
|
||||||
// Send
|
// Send
|
||||||
tx.send_async(Dispatch::Announcement(val)).await.ok();
|
tx.send_async(Dispatch::Announcement(val)).await.ok();
|
||||||
}
|
}
|
||||||
@@ -198,7 +177,7 @@ impl PersonRegistry {
|
|||||||
loop {
|
loop {
|
||||||
match flume::Selector::new()
|
match flume::Selector::new()
|
||||||
.recv(rx, |result| result.ok())
|
.recv(rx, |result| result.ok())
|
||||||
.wait_timeout(Duration::from_secs(2))
|
.wait_timeout(Duration::from_secs(TIMEOUT))
|
||||||
{
|
{
|
||||||
Ok(Some(public_key)) => {
|
Ok(Some(public_key)) => {
|
||||||
batch.insert(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
|
/// Set profile encryption keys announcement
|
||||||
fn set_announcement(&mut self, event: &Event, cx: &mut App) {
|
fn set_announcement(&mut self, event: &Event, cx: &mut App) {
|
||||||
let announcement = Announcement::from(event);
|
let announcement = Announcement::from(event);
|
||||||
@@ -334,19 +342,3 @@ where
|
|||||||
|
|
||||||
Ok(())
|
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)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -283,7 +283,10 @@ impl RelayAuth {
|
|||||||
});
|
});
|
||||||
|
|
||||||
window.push_notification(
|
window.push_notification(
|
||||||
Notification::success(format!("{} has been authenticated", url)),
|
Notification::success(format!(
|
||||||
|
"{} has been authenticated",
|
||||||
|
url.domain().unwrap_or_default()
|
||||||
|
)),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"];
|
pub const SEARCH_RELAYS: [&str; 2] = ["wss://antiprimal.net", "wss://search.nos.today"];
|
||||||
|
|
||||||
/// Default bootstrap relays
|
/// Default bootstrap relays
|
||||||
pub const BOOTSTRAP_RELAYS: [&str; 3] = [
|
pub const BOOTSTRAP_RELAYS: [&str; 4] = [
|
||||||
|
"wss://relay.damus.io",
|
||||||
"wss://relay.primal.net",
|
"wss://relay.primal.net",
|
||||||
"wss://indexer.coracle.social",
|
"wss://indexer.coracle.social",
|
||||||
"wss://user.kindpag.es",
|
"wss://user.kindpag.es",
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ impl NostrRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Connect to all added relays
|
// Connect to all added relays
|
||||||
client.connect().and_wait(Duration::from_secs(2)).await;
|
client.connect().await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -405,12 +405,12 @@ impl Render for Notification {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
let opacity = delta;
|
||||||
let y_offset = match placement {
|
let y_offset = match placement {
|
||||||
placement if placement.is_top() => px(-45.) + delta * px(45.),
|
placement if placement.is_top() => px(-45.) + delta * px(45.),
|
||||||
placement if placement.is_bottom() => px(45.) - delta * px(45.),
|
placement if placement.is_bottom() => px(45.) - delta * px(45.),
|
||||||
_ => px(0.),
|
_ => px(0.),
|
||||||
};
|
};
|
||||||
let opacity = delta;
|
|
||||||
this.top(px(0.) + y_offset)
|
this.top(px(0.) + y_offset)
|
||||||
.opacity(opacity)
|
.opacity(opacity)
|
||||||
.when(opacity < 0.85, |this| this.shadow_none())
|
.when(opacity < 0.85, |this| this.shadow_none())
|
||||||
|
|||||||
Reference in New Issue
Block a user