chore: follow up on nip-4e (#195)
* update deps * . * remove resend button * clean up * . * . * . * . * .
This commit is contained in:
39
Cargo.lock
generated
39
Cargo.lock
generated
@@ -1157,7 +1157,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collections"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#fd3ca0303ff1dfc552119878724eb151bc94b49c"
|
||||
source = "git+https://github.com/zed-industries/zed#f503c65924fff9799b359b6c83994b2eaf7af7c8"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"rustc-hash 2.1.1",
|
||||
@@ -1597,7 +1597,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "derive_refineable"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#fd3ca0303ff1dfc552119878724eb151bc94b49c"
|
||||
source = "git+https://github.com/zed-industries/zed#f503c65924fff9799b359b6c83994b2eaf7af7c8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2494,7 +2494,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui"
|
||||
version = "0.2.2"
|
||||
source = "git+https://github.com/zed-industries/zed#fd3ca0303ff1dfc552119878724eb151bc94b49c"
|
||||
source = "git+https://github.com/zed-industries/zed#f503c65924fff9799b359b6c83994b2eaf7af7c8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"as-raw-xcb-connection",
|
||||
@@ -2589,7 +2589,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui_macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#fd3ca0303ff1dfc552119878724eb151bc94b49c"
|
||||
source = "git+https://github.com/zed-industries/zed#f503c65924fff9799b359b6c83994b2eaf7af7c8"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@@ -2600,7 +2600,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui_tokio"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#fd3ca0303ff1dfc552119878724eb151bc94b49c"
|
||||
source = "git+https://github.com/zed-industries/zed#f503c65924fff9799b359b6c83994b2eaf7af7c8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gpui",
|
||||
@@ -2829,7 +2829,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "http_client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#fd3ca0303ff1dfc552119878724eb151bc94b49c"
|
||||
source = "git+https://github.com/zed-industries/zed#f503c65924fff9799b359b6c83994b2eaf7af7c8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-compression",
|
||||
@@ -2854,7 +2854,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "http_client_tls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#fd3ca0303ff1dfc552119878724eb151bc94b49c"
|
||||
source = "git+https://github.com/zed-industries/zed#f503c65924fff9799b359b6c83994b2eaf7af7c8"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"rustls-platform-verifier",
|
||||
@@ -3672,7 +3672,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "media"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#fd3ca0303ff1dfc552119878724eb151bc94b49c"
|
||||
source = "git+https://github.com/zed-industries/zed#f503c65924fff9799b359b6c83994b2eaf7af7c8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bindgen 0.71.1",
|
||||
@@ -4514,7 +4514,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
[[package]]
|
||||
name = "perf"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#fd3ca0303ff1dfc552119878724eb151bc94b49c"
|
||||
source = "git+https://github.com/zed-industries/zed#f503c65924fff9799b359b6c83994b2eaf7af7c8"
|
||||
dependencies = [
|
||||
"collections",
|
||||
"serde",
|
||||
@@ -5130,7 +5130,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "refineable"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#fd3ca0303ff1dfc552119878724eb151bc94b49c"
|
||||
source = "git+https://github.com/zed-industries/zed#f503c65924fff9799b359b6c83994b2eaf7af7c8"
|
||||
dependencies = [
|
||||
"derive_refineable",
|
||||
]
|
||||
@@ -5237,7 +5237,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "reqwest_client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#fd3ca0303ff1dfc552119878724eb151bc94b49c"
|
||||
source = "git+https://github.com/zed-industries/zed#f503c65924fff9799b359b6c83994b2eaf7af7c8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -5291,13 +5291,12 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rope"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#fd3ca0303ff1dfc552119878724eb151bc94b49c"
|
||||
source = "git+https://github.com/zed-industries/zed#f503c65924fff9799b359b6c83994b2eaf7af7c8"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"log",
|
||||
"rayon",
|
||||
"regex",
|
||||
"smallvec",
|
||||
"sum_tree",
|
||||
"unicode-segmentation",
|
||||
"util",
|
||||
@@ -5494,9 +5493,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.12.0"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
|
||||
checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a"
|
||||
dependencies = [
|
||||
"web-time",
|
||||
"zeroize",
|
||||
@@ -5759,7 +5758,7 @@ checksum = "16c2f82143577edb4921b71ede051dac62ca3c16084e918bf7b40c96ae10eb33"
|
||||
[[package]]
|
||||
name = "semantic_version"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#fd3ca0303ff1dfc552119878724eb151bc94b49c"
|
||||
source = "git+https://github.com/zed-industries/zed#f503c65924fff9799b359b6c83994b2eaf7af7c8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
@@ -6132,6 +6131,8 @@ dependencies = [
|
||||
"nostr-lmdb",
|
||||
"nostr-sdk",
|
||||
"rustls",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smol",
|
||||
"whoami",
|
||||
]
|
||||
@@ -6203,7 +6204,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
[[package]]
|
||||
name = "sum_tree"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#fd3ca0303ff1dfc552119878724eb151bc94b49c"
|
||||
source = "git+https://github.com/zed-industries/zed#f503c65924fff9799b359b6c83994b2eaf7af7c8"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"log",
|
||||
@@ -7239,7 +7240,7 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
[[package]]
|
||||
name = "util"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#fd3ca0303ff1dfc552119878724eb151bc94b49c"
|
||||
source = "git+https://github.com/zed-industries/zed#f503c65924fff9799b359b6c83994b2eaf7af7c8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-fs",
|
||||
@@ -7274,7 +7275,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "util_macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#fd3ca0303ff1dfc552119878724eb151bc94b49c"
|
||||
source = "git+https://github.com/zed-industries/zed#f503c65924fff9799b359b6c83994b2eaf7af7c8"
|
||||
dependencies = [
|
||||
"perf",
|
||||
"quote",
|
||||
|
||||
@@ -4,7 +4,7 @@ use cargo_packager_updater::{check_update, Config, Update};
|
||||
use gpui::http_client::Url;
|
||||
use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task, Window};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use states::constants::{APP_PUBKEY, APP_UPDATER_ENDPOINT};
|
||||
use states::{APP_PUBKEY, APP_UPDATER_ENDPOINT};
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
AutoUpdater::set_global(cx.new(AutoUpdater::new), cx);
|
||||
|
||||
@@ -6,7 +6,7 @@ use gpui::{Image, ImageFormat, SharedString, SharedUri};
|
||||
use nostr_sdk::prelude::*;
|
||||
use qrcode::render::svg;
|
||||
use qrcode::QrCode;
|
||||
use states::constants::IMAGE_RESIZE_SERVICE;
|
||||
use states::IMAGE_RESIZE_SERVICE;
|
||||
|
||||
const NOW: &str = "now";
|
||||
const SECONDS_IN_MINUTE: i64 = 60;
|
||||
|
||||
@@ -21,9 +21,10 @@ use nostr_sdk::prelude::*;
|
||||
use registry::{Registry, RegistryEvent};
|
||||
use settings::AppSettings;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use states::constants::{BOOTSTRAP_RELAYS, DEFAULT_SIDEBAR_WIDTH};
|
||||
use states::state::{Announcement, AuthRequest, Response, SignalKind, UnwrappingStatus};
|
||||
use states::{app_state, default_nip17_relays, default_nip65_relays};
|
||||
use states::{
|
||||
app_state, default_nip17_relays, default_nip65_relays, Announcement, AuthRequest, Response,
|
||||
SignalKind, UnwrappingStatus, BOOTSTRAP_RELAYS, DEFAULT_SIDEBAR_WIDTH,
|
||||
};
|
||||
use theme::{ActiveTheme, Theme, ThemeMode};
|
||||
use title_bar::TitleBar;
|
||||
use ui::actions::{CopyPublicKey, OpenPublicKey};
|
||||
@@ -324,7 +325,7 @@ impl ChatSpace {
|
||||
}
|
||||
|
||||
fn load_encryption(&self, ann: Announcement, window: &Window, cx: &Context<Self>) {
|
||||
log::info!("Loading encryption keys: {ann:?}");
|
||||
log::info!("Found encryption announcement: {ann:?}");
|
||||
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
let state = app_state();
|
||||
|
||||
@@ -6,8 +6,7 @@ use gpui::{
|
||||
TitlebarOptions, WindowBackgroundAppearance, WindowBounds, WindowDecorations, WindowKind,
|
||||
WindowOptions,
|
||||
};
|
||||
use states::app_state;
|
||||
use states::constants::{APP_ID, CLIENT_NAME};
|
||||
use states::{app_state, APP_ID, CLIENT_NAME};
|
||||
use ui::Root;
|
||||
|
||||
use crate::actions::{load_embedded_fonts, quit, Quit};
|
||||
|
||||
@@ -14,8 +14,7 @@ use key_store::KeyStore;
|
||||
use nostr_connect::prelude::*;
|
||||
use registry::Registry;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use states::app_state;
|
||||
use states::constants::BUNKER_TIMEOUT;
|
||||
use states::{app_state, BUNKER_TIMEOUT};
|
||||
use theme::ActiveTheme;
|
||||
use ui::avatar::Avatar;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
|
||||
@@ -17,13 +17,13 @@ use indexset::{BTreeMap, BTreeSet};
|
||||
use itertools::Itertools;
|
||||
use nostr_sdk::prelude::*;
|
||||
use registry::message::{Message, RenderedMessage};
|
||||
use registry::room::{Room, RoomKind, RoomSignal, SendOptions, SendReport, SignerKind};
|
||||
use registry::room::{Room, RoomKind, RoomSignal, SendOptions, SendReport};
|
||||
use registry::Registry;
|
||||
use serde::Deserialize;
|
||||
use settings::AppSettings;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use smol::fs;
|
||||
use states::app_state;
|
||||
use states::{app_state, SignerKind};
|
||||
use theme::ActiveTheme;
|
||||
use ui::actions::{CopyPublicKey, OpenPublicKey};
|
||||
use ui::avatar::Avatar;
|
||||
@@ -107,6 +107,15 @@ impl Chat {
|
||||
let mut subscriptions = smallvec![];
|
||||
let mut tasks = smallvec![];
|
||||
|
||||
tasks.push(
|
||||
// Get messaging relays and encryption keys announcement for all members
|
||||
cx.background_spawn(async move {
|
||||
if let Err(e) = connect.await {
|
||||
log::error!("Failed to initialize room: {e}");
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
tasks.push(
|
||||
// Load all messages belonging to this room
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
@@ -126,15 +135,6 @@ impl Chat {
|
||||
}),
|
||||
);
|
||||
|
||||
tasks.push(
|
||||
// Get messaging relays and encryption keys announcement for all members
|
||||
cx.background_spawn(async move {
|
||||
if let Err(e) = connect.await {
|
||||
log::error!("Failed to initialize room: {e}");
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
subscriptions.push(
|
||||
// Subscribe to input events
|
||||
cx.subscribe_in(
|
||||
@@ -346,6 +346,7 @@ impl Chat {
|
||||
}
|
||||
|
||||
/// Resend a failed message
|
||||
#[allow(dead_code)]
|
||||
fn resend_message(&mut self, id: &EventId, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if let Some(reports) = self.reports_by_id.get(id).cloned() {
|
||||
let id_clone = id.to_owned();
|
||||
@@ -705,23 +706,7 @@ impl Chat {
|
||||
})
|
||||
.child(text)
|
||||
.when(is_sent_failed, |this| {
|
||||
this.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.child(self.render_message_reports(&id, cx))
|
||||
.child(
|
||||
Button::new(SharedString::from(id.to_hex()))
|
||||
.label(t!("common.resend"))
|
||||
.danger()
|
||||
.xsmall()
|
||||
.rounded()
|
||||
.on_click(cx.listener(
|
||||
move |this, _, window, cx| {
|
||||
this.resend_message(&id, window, cx);
|
||||
},
|
||||
)),
|
||||
),
|
||||
)
|
||||
this.child(self.render_message_reports(&id, cx))
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -17,8 +17,7 @@ use registry::room::Room;
|
||||
use registry::Registry;
|
||||
use settings::AppSettings;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use states::app_state;
|
||||
use states::constants::BOOTSTRAP_RELAYS;
|
||||
use states::{app_state, BOOTSTRAP_RELAYS};
|
||||
use theme::ActiveTheme;
|
||||
use ui::avatar::Avatar;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
|
||||
@@ -11,8 +11,7 @@ use key_store::backend::KeyItem;
|
||||
use key_store::KeyStore;
|
||||
use nostr_connect::prelude::*;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use states::app_state;
|
||||
use states::constants::BUNKER_TIMEOUT;
|
||||
use states::{app_state, BUNKER_TIMEOUT};
|
||||
use theme::ActiveTheme;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
use ui::dock_area::panel::{Panel, PanelEvent};
|
||||
|
||||
@@ -12,8 +12,7 @@ use key_store::KeyStore;
|
||||
use nostr_sdk::prelude::*;
|
||||
use settings::AppSettings;
|
||||
use smol::fs;
|
||||
use states::constants::BOOTSTRAP_RELAYS;
|
||||
use states::{app_state, default_nip17_relays, default_nip65_relays};
|
||||
use states::{app_state, default_nip17_relays, default_nip65_relays, BOOTSTRAP_RELAYS};
|
||||
use theme::ActiveTheme;
|
||||
use ui::avatar::Avatar;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
|
||||
@@ -13,8 +13,7 @@ use key_store::backend::KeyItem;
|
||||
use key_store::KeyStore;
|
||||
use nostr_connect::prelude::*;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use states::app_state;
|
||||
use states::constants::{CLIENT_NAME, NOSTR_CONNECT_RELAY, NOSTR_CONNECT_TIMEOUT};
|
||||
use states::{app_state, CLIENT_NAME, NOSTR_CONNECT_RELAY, NOSTR_CONNECT_TIMEOUT};
|
||||
use theme::ActiveTheme;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
use ui::dock_area::panel::{Panel, PanelEvent};
|
||||
|
||||
@@ -13,8 +13,7 @@ use nostr_sdk::prelude::*;
|
||||
use registry::Registry;
|
||||
use settings::AppSettings;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use states::app_state;
|
||||
use states::constants::BOOTSTRAP_RELAYS;
|
||||
use states::{app_state, BOOTSTRAP_RELAYS};
|
||||
use theme::ActiveTheme;
|
||||
use ui::avatar::Avatar;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
|
||||
@@ -20,8 +20,7 @@ use registry::room::{Room, RoomKind};
|
||||
use registry::{Registry, RegistryEvent};
|
||||
use settings::AppSettings;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use states::app_state;
|
||||
use states::constants::{BOOTSTRAP_RELAYS, SEARCH_RELAYS};
|
||||
use states::{app_state, BOOTSTRAP_RELAYS, SEARCH_RELAYS};
|
||||
use theme::ActiveTheme;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
use ui::dock_area::panel::{Panel, PanelEvent};
|
||||
@@ -626,7 +625,7 @@ impl Sidebar {
|
||||
.name(this.display_name(cx))
|
||||
.avatar(this.display_image(proxy, cx))
|
||||
.created_at(this.created_at.to_ago())
|
||||
.public_key(this.members.iter().nth(0).unwrap().0)
|
||||
.public_key(&this.members[0])
|
||||
.kind(this.kind)
|
||||
.on_click(handler),
|
||||
)
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::pin::Pin;
|
||||
use anyhow::Result;
|
||||
use futures::FutureExt as _;
|
||||
use gpui::AsyncApp;
|
||||
use states::paths::config_dir;
|
||||
use states::config_dir;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum KeyItem {
|
||||
|
||||
@@ -245,7 +245,7 @@ impl Registry {
|
||||
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_key(&public_key))
|
||||
.filter(|room| room.read(cx).members.contains(&public_key))
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -8,20 +8,10 @@ use common::display::RenderedProfile;
|
||||
use common::event::EventUtils;
|
||||
use gpui::{App, AppContext, Context, EventEmitter, SharedString, SharedUri, Task};
|
||||
use nostr_sdk::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use states::app_state;
|
||||
use states::constants::SEND_RETRY;
|
||||
use states::{app_state, SignerKind, SEND_RETRY};
|
||||
|
||||
use crate::Registry;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Deserialize, Serialize)]
|
||||
pub enum SignerKind {
|
||||
Encryption,
|
||||
User,
|
||||
#[default]
|
||||
Auto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct SendOptions {
|
||||
pub backup: bool,
|
||||
@@ -127,8 +117,6 @@ pub enum RoomKind {
|
||||
Request,
|
||||
}
|
||||
|
||||
type DevicePublicKey = PublicKey;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Room {
|
||||
pub id: u64,
|
||||
@@ -136,7 +124,7 @@ pub struct Room {
|
||||
/// Subject of the room
|
||||
pub subject: Option<String>,
|
||||
/// All members of the room
|
||||
pub members: HashMap<PublicKey, Option<DevicePublicKey>>,
|
||||
pub members: Vec<PublicKey>,
|
||||
/// Kind
|
||||
pub kind: RoomKind,
|
||||
}
|
||||
@@ -175,11 +163,7 @@ impl From<&Event> for Room {
|
||||
let created_at = val.created_at;
|
||||
|
||||
// Get the members from the event's tags and event's pubkey
|
||||
let members: HashMap<PublicKey, Option<DevicePublicKey>> = val
|
||||
.all_pubkeys()
|
||||
.into_iter()
|
||||
.map(|public_key| (public_key, None))
|
||||
.collect();
|
||||
let members = val.all_pubkeys();
|
||||
|
||||
// Get subject from tags
|
||||
let subject = val
|
||||
@@ -203,11 +187,7 @@ impl From<&UnsignedEvent> for Room {
|
||||
let created_at = val.created_at;
|
||||
|
||||
// Get the members from the event's tags and event's pubkey
|
||||
let members: HashMap<PublicKey, Option<DevicePublicKey>> = val
|
||||
.all_pubkeys()
|
||||
.into_iter()
|
||||
.map(|public_key| (public_key, None))
|
||||
.collect();
|
||||
let members = val.all_pubkeys();
|
||||
|
||||
// Get subject from tags
|
||||
let subject = val
|
||||
@@ -289,7 +269,7 @@ impl Room {
|
||||
|
||||
/// Returns the members of the room
|
||||
pub fn members(&self) -> Vec<PublicKey> {
|
||||
self.members.keys().cloned().collect()
|
||||
self.members.clone()
|
||||
}
|
||||
|
||||
/// Checks if the room has more than two members (group)
|
||||
@@ -324,9 +304,9 @@ impl Room {
|
||||
|
||||
let target_member = self
|
||||
.members
|
||||
.keys()
|
||||
.iter()
|
||||
.find(|&member| Some(member) != signer_pubkey.as_ref())
|
||||
.or_else(|| self.members.keys().next())
|
||||
.or_else(|| self.members.first())
|
||||
.expect("Room should have at least one member");
|
||||
|
||||
registry.read(cx).get_person(target_member, cx)
|
||||
@@ -339,7 +319,7 @@ impl Room {
|
||||
if self.is_group() {
|
||||
let profiles: Vec<Profile> = self
|
||||
.members
|
||||
.keys()
|
||||
.iter()
|
||||
.map(|public_key| registry.get_person(public_key, cx))
|
||||
.collect();
|
||||
|
||||
@@ -447,7 +427,7 @@ impl Room {
|
||||
// Add receivers
|
||||
//
|
||||
// NOTE: current user will be removed from the list of receivers
|
||||
for (member, _) in self.members.iter() {
|
||||
for member in self.members.iter() {
|
||||
// Get relay hint if available
|
||||
let relay_url = relay_cache
|
||||
.get(member)
|
||||
@@ -505,54 +485,75 @@ impl Room {
|
||||
opts: &SendOptions,
|
||||
cx: &App,
|
||||
) -> Task<Result<Vec<SendReport>, Error>> {
|
||||
let mut members = self.members.clone();
|
||||
let rumor = rumor.to_owned();
|
||||
let opts = opts.to_owned();
|
||||
|
||||
// Get all members
|
||||
let mut members = self.members();
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let state = app_state();
|
||||
|
||||
let client = state.client();
|
||||
let signer_kind = opts.signer_kind;
|
||||
|
||||
let relay_cache = state.relay_cache.read().await;
|
||||
let device = state.device.read().await.encryption.clone();
|
||||
let announcement_cache = state.announcement_cache.read().await;
|
||||
|
||||
let encryption = state.device.read().await.encryption.clone();
|
||||
// Get the encryption public key
|
||||
let encryption_pubkey = if let Some(signer) = encryption.as_ref() {
|
||||
signer.get_public_key().await.ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let user_signer = client.signer().await?;
|
||||
let user_pubkey = user_signer.get_public_key().await?;
|
||||
|
||||
// Remove the current user's public key from the list of receivers
|
||||
// Current user will be handled separately
|
||||
let (public_key, device_pubkey) = members.remove_entry(&user_pubkey).unwrap();
|
||||
// the current user will be handled separately
|
||||
members.retain(|&pk| pk != user_pubkey);
|
||||
|
||||
// Determine the signer will be used based on the provided options
|
||||
let (signer, signer_kind) =
|
||||
Self::select_signer(&opts.signer_kind, device, user_signer)?;
|
||||
let signer = Self::select_signer(&opts.signer_kind, user_signer, encryption)?;
|
||||
|
||||
// Collect the send reports
|
||||
let mut reports: Vec<SendReport> = vec![];
|
||||
|
||||
for (user, device_pubkey) in members.into_iter() {
|
||||
let urls = relay_cache.get(&user).cloned().unwrap_or_default();
|
||||
for member in members.into_iter() {
|
||||
// Get user's messaging relays
|
||||
let urls = relay_cache.get(&member).cloned().unwrap_or_default();
|
||||
|
||||
// Check if there are any relays to send the message to
|
||||
if urls.is_empty() {
|
||||
reports.push(SendReport::new(user).relays_not_found());
|
||||
reports.push(SendReport::new(member).relays_not_found());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip sending if using encryption keys but device not found
|
||||
if device_pubkey.is_none() && matches!(opts.signer_kind, SignerKind::Encryption) {
|
||||
reports.push(SendReport::new(user).device_not_found());
|
||||
// Ensure connection to the relays
|
||||
for url in urls.iter() {
|
||||
client.add_relay(url).await.ok();
|
||||
client.connect_relay(url).await.ok();
|
||||
}
|
||||
|
||||
// Get user's encryption public key if available
|
||||
let encryption = announcement_cache
|
||||
.get(&member)
|
||||
.and_then(|a| a.to_owned().map(|a| a.public_key()));
|
||||
|
||||
// Skip sending if using encryption signer but receiver's encryption keys not found
|
||||
if encryption.is_none() && matches!(signer_kind, SignerKind::Encryption) {
|
||||
reports.push(SendReport::new(member).device_not_found());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine the receiver based on the signer kind
|
||||
let receiver = Self::select_receiver(&opts.signer_kind, user, device_pubkey)?;
|
||||
let receiver = Self::select_receiver(&signer_kind, member, encryption)?;
|
||||
let rumor = rumor.clone();
|
||||
|
||||
// Construct the gift wrap event
|
||||
let rumor = rumor.clone();
|
||||
let event = EventBuilder::gift_wrap(&signer, &receiver, rumor, []).await?;
|
||||
|
||||
// Send the event to the messaging relays
|
||||
// Send the gift wrap event to the messaging relays
|
||||
match client.send_event_to(urls, &event).await {
|
||||
Ok(output) => {
|
||||
let id = output.id().to_owned();
|
||||
@@ -590,33 +591,32 @@ impl Room {
|
||||
}
|
||||
}
|
||||
|
||||
// Select a signer based on previous usage, not from the options
|
||||
let receiver = Self::select_receiver(&signer_kind, public_key, device_pubkey)?;
|
||||
|
||||
// Construct a gift wrap to back up to current user's owned messaging relays
|
||||
let receiver = Self::select_receiver(&signer_kind, user_pubkey, encryption_pubkey)?;
|
||||
let rumor = rumor.clone();
|
||||
|
||||
// Construct the gift-wrapped event
|
||||
let event = EventBuilder::gift_wrap(&signer, &receiver, rumor, []).await?;
|
||||
|
||||
// Only send a backup message to current user if sent successfully to others
|
||||
if opts.backup() && reports.iter().all(|r| r.is_sent_success()) {
|
||||
let urls = relay_cache.get(&public_key).cloned().unwrap_or_default();
|
||||
let urls = relay_cache.get(&user_pubkey).cloned().unwrap_or_default();
|
||||
|
||||
// Check if there are any relays to send the event to
|
||||
if urls.is_empty() {
|
||||
reports.push(SendReport::new(public_key).relays_not_found());
|
||||
reports.push(SendReport::new(user_pubkey).relays_not_found());
|
||||
} else {
|
||||
// Send the event to the messaging relays
|
||||
match client.send_event_to(urls, &event).await {
|
||||
Ok(output) => {
|
||||
reports.push(SendReport::new(public_key).status(output));
|
||||
reports.push(SendReport::new(user_pubkey).status(output));
|
||||
}
|
||||
Err(e) => {
|
||||
reports.push(SendReport::new(public_key).error(e.to_string()));
|
||||
reports.push(SendReport::new(user_pubkey).error(e.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reports.push(SendReport::new(public_key).on_hold(event));
|
||||
reports.push(SendReport::new(user_pubkey).on_hold(event));
|
||||
}
|
||||
|
||||
Ok(reports)
|
||||
@@ -683,37 +683,30 @@ impl Room {
|
||||
})
|
||||
}
|
||||
|
||||
fn select_signer<T>(
|
||||
kind: &SignerKind,
|
||||
device: Option<T>,
|
||||
user: T,
|
||||
) -> Result<(T, SignerKind), Error>
|
||||
fn select_signer<T>(kind: &SignerKind, user: T, encryption: Option<T>) -> Result<T, Error>
|
||||
where
|
||||
T: NostrSigner,
|
||||
{
|
||||
match kind {
|
||||
SignerKind::Auto => device
|
||||
.map(|d| (d, SignerKind::Encryption))
|
||||
.or(Some((user, SignerKind::User)))
|
||||
.ok_or_else(|| anyhow!("No signer available")),
|
||||
SignerKind::Encryption => device
|
||||
.map(|d| (d, SignerKind::Encryption))
|
||||
.ok_or_else(|| anyhow!("No encryption key found")),
|
||||
SignerKind::User => Ok((user, SignerKind::User)),
|
||||
SignerKind::Encryption => {
|
||||
Ok(encryption.ok_or_else(|| anyhow!("No encryption key found"))?)
|
||||
}
|
||||
SignerKind::User => Ok(user),
|
||||
SignerKind::Auto => Ok(encryption.unwrap_or(user)),
|
||||
}
|
||||
}
|
||||
|
||||
fn select_receiver(
|
||||
kind: &SignerKind,
|
||||
user: PublicKey,
|
||||
device: Option<PublicKey>,
|
||||
encryption: Option<PublicKey>,
|
||||
) -> Result<PublicKey, Error> {
|
||||
match kind {
|
||||
SignerKind::Encryption => {
|
||||
Ok(device.ok_or_else(|| anyhow!("Receiver's encryption key not found"))?)
|
||||
Ok(encryption.ok_or_else(|| anyhow!("Receiver's encryption key not found"))?)
|
||||
}
|
||||
SignerKind::User => Ok(user),
|
||||
SignerKind::Auto => Ok(device.unwrap_or(user)),
|
||||
SignerKind::Auto => Ok(encryption.unwrap_or(user)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@ use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task};
|
||||
use nostr_sdk::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use states::app_state;
|
||||
use states::constants::SETTINGS_IDENTIFIER;
|
||||
use states::{app_state, SETTINGS_IDENTIFIER};
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
let state = cx.new(AppSettings::new);
|
||||
|
||||
@@ -13,6 +13,8 @@ smol.workspace = true
|
||||
flume.workspace = true
|
||||
log.workspace = true
|
||||
anyhow.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
whoami = "1.6.1"
|
||||
rustls = "0.23.23"
|
||||
|
||||
@@ -3,12 +3,13 @@ use std::sync::OnceLock;
|
||||
use nostr_sdk::prelude::*;
|
||||
use whoami::{devicename, platform};
|
||||
|
||||
use crate::constants::CLIENT_NAME;
|
||||
use crate::state::AppState;
|
||||
mod constants;
|
||||
mod paths;
|
||||
mod state;
|
||||
|
||||
pub mod constants;
|
||||
pub mod paths;
|
||||
pub mod state;
|
||||
pub use constants::*;
|
||||
pub use paths::*;
|
||||
pub use state::*;
|
||||
|
||||
static APP_STATE: OnceLock<AppState> = OnceLock::new();
|
||||
static APP_NAME: OnceLock<String> = OnceLock::new();
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use nostr_sdk::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Deserialize, Serialize)]
|
||||
pub enum SignerKind {
|
||||
Encryption,
|
||||
#[default]
|
||||
User,
|
||||
Auto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Device {
|
||||
|
||||
@@ -15,7 +15,6 @@ use crate::constants::{
|
||||
BOOTSTRAP_RELAYS, METADATA_BATCH_LIMIT, METADATA_BATCH_TIMEOUT, QUERY_TIMEOUT, SEARCH_RELAYS,
|
||||
};
|
||||
use crate::paths::config_dir;
|
||||
use crate::state::device::Device;
|
||||
use crate::state::ingester::Ingester;
|
||||
use crate::state::tracker::EventTracker;
|
||||
|
||||
@@ -24,6 +23,7 @@ mod ingester;
|
||||
mod signal;
|
||||
mod tracker;
|
||||
|
||||
pub use device::*;
|
||||
pub use signal::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -43,6 +43,9 @@ pub struct AppState {
|
||||
/// Cache of messaging relays for each public key
|
||||
pub relay_cache: RwLock<HashMap<PublicKey, HashSet<RelayUrl>>>,
|
||||
|
||||
/// Cache of device announcement for each public key
|
||||
pub announcement_cache: RwLock<HashMap<PublicKey, Option<Announcement>>>,
|
||||
|
||||
/// NIP-4e: https://github.com/nostr-protocol/nips/blob/per-device-keys/4e.md
|
||||
pub device: RwLock<Device>,
|
||||
|
||||
@@ -83,6 +86,7 @@ impl AppState {
|
||||
let device = RwLock::new(Device::default());
|
||||
let event_tracker = RwLock::new(EventTracker::default());
|
||||
let relay_cache = RwLock::new(HashMap::default());
|
||||
let announcement_cache = RwLock::new(HashMap::default());
|
||||
|
||||
let signal = Signal::default();
|
||||
let ingester = Ingester::default();
|
||||
@@ -92,6 +96,7 @@ impl AppState {
|
||||
device,
|
||||
event_tracker,
|
||||
relay_cache,
|
||||
announcement_cache,
|
||||
signal,
|
||||
ingester,
|
||||
initialized_at: Timestamp::now(),
|
||||
@@ -138,6 +143,9 @@ impl AppState {
|
||||
// Get user's gossip relays
|
||||
self.get_nip65(pk).await.ok();
|
||||
|
||||
// Initialize the relay and announcement caches
|
||||
self.init_cache().await.ok();
|
||||
|
||||
// Initialize client keys
|
||||
self.init_client_keys().await.ok();
|
||||
|
||||
@@ -236,12 +244,16 @@ impl AppState {
|
||||
match event.kind {
|
||||
// Encryption Keys announcement event
|
||||
Kind::Custom(10044) => {
|
||||
if let Ok(true) = self.is_self_authored(&event).await {
|
||||
if let Ok(announcement) = self.extract_announcement(&event) {
|
||||
if let Ok(announcement) = self.extract_announcement(&event) {
|
||||
if let Ok(true) = self.is_self_authored(&event).await {
|
||||
self.signal
|
||||
.send(SignalKind::EncryptionSet(announcement))
|
||||
.send(SignalKind::EncryptionSet(announcement.clone()))
|
||||
.await;
|
||||
}
|
||||
|
||||
// Cache the announcement for further queries
|
||||
let mut announcement_cache = self.announcement_cache.write().await;
|
||||
announcement_cache.insert(event.pubkey, Some(announcement));
|
||||
}
|
||||
}
|
||||
// Encryption Keys request event
|
||||
@@ -572,6 +584,33 @@ impl AppState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialize the relay and announcement caches with events from the local database
|
||||
pub async fn init_cache(&self) -> Result<(), Error> {
|
||||
let filter = Filter::new().kind(Kind::InboxRelays);
|
||||
let events = self.client.database().query(filter).await?;
|
||||
let mut relay_cache = self.relay_cache.write().await;
|
||||
|
||||
for event in events.into_iter() {
|
||||
let relays: Vec<RelayUrl> =
|
||||
nip17::extract_relay_list(&event).take(3).cloned().collect();
|
||||
|
||||
// Push all relays to the relay cache
|
||||
relay_cache.entry(event.pubkey).or_default().extend(relays);
|
||||
}
|
||||
|
||||
let filter = Filter::new().kind(Kind::Custom(10044));
|
||||
let events = self.client.database().query(filter).await?;
|
||||
let mut announcement_cache = self.announcement_cache.write().await;
|
||||
|
||||
for event in events.into_iter() {
|
||||
if let Ok(announcement) = self.extract_announcement(&event) {
|
||||
announcement_cache.insert(event.pubkey, Some(announcement));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialize the client keys to communicate between clients
|
||||
///
|
||||
/// NIP-4e: https://github.com/nostr-protocol/nips/blob/per-device-keys/4e.md
|
||||
@@ -961,7 +1000,7 @@ impl AppState {
|
||||
|
||||
// Subscribe to gift wrap events
|
||||
self.client
|
||||
.subscribe_with_id_to(urls, id, filter, None)
|
||||
.subscribe_with_id_to(&urls, id, filter, None)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
@@ -1037,22 +1076,22 @@ impl AppState {
|
||||
}
|
||||
|
||||
// Try to unwrap with the available signer
|
||||
if let Ok(unwrapped) = self.try_unwrap_gift_wrap(gift_wrap).await {
|
||||
let sender = unwrapped.sender;
|
||||
let mut rumor_unsigned = unwrapped.rumor;
|
||||
let unwrapped = self.try_unwrap_gift_wrap(gift_wrap).await?;
|
||||
let sender = unwrapped.sender;
|
||||
let mut rumor_unsigned = unwrapped.rumor;
|
||||
|
||||
if !self.verify_sender(sender, &rumor_unsigned).await {
|
||||
return Err(anyhow!("Invalid rumor"));
|
||||
};
|
||||
if !self.verify_sender(sender, &rumor_unsigned).await {
|
||||
return Err(anyhow!("Invalid rumor"));
|
||||
};
|
||||
|
||||
// Generate event id for the rumor if it doesn't have one
|
||||
rumor_unsigned.ensure_id();
|
||||
// Generate event id for the rumor if it doesn't have one
|
||||
rumor_unsigned.ensure_id();
|
||||
|
||||
self.set_rumor(gift_wrap.id, &rumor_unsigned).await?;
|
||||
self.process_rumor(gift_wrap.id, rumor_unsigned).await?;
|
||||
// Cache the rumor
|
||||
self.set_rumor(gift_wrap.id, &rumor_unsigned).await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
// Process the rumor
|
||||
self.process_rumor(gift_wrap.id, rumor_unsigned).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1062,20 +1101,21 @@ impl AppState {
|
||||
// Try to unwrap with the device's encryption keys first
|
||||
// NIP-4e: https://github.com/nostr-protocol/nips/blob/per-device-keys/4e.md
|
||||
if let Some(signer) = self.device.read().await.encryption.as_ref() {
|
||||
if let Ok(unwrapped) = UnwrappedGift::from_gift_wrap(signer, gift_wrap).await {
|
||||
return Ok(unwrapped);
|
||||
match UnwrappedGift::from_gift_wrap(signer, gift_wrap).await {
|
||||
Ok(unwrapped) => {
|
||||
return Ok(unwrapped);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to unwrap with the encryption key: {e}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get user's signer
|
||||
let signer = self.client.signer().await?;
|
||||
|
||||
// Try to unwrap with the user's signer
|
||||
if let Ok(unwrapped) = UnwrappedGift::from_gift_wrap(&signer, gift_wrap).await {
|
||||
return Ok(unwrapped);
|
||||
}
|
||||
let signer = self.client.signer().await?;
|
||||
let unwrapped = UnwrappedGift::from_gift_wrap(&signer, gift_wrap).await?;
|
||||
|
||||
Err(anyhow!("No signer available"))
|
||||
Ok(unwrapped)
|
||||
}
|
||||
|
||||
/// Process a rumor event.
|
||||
|
||||
Reference in New Issue
Block a user