feat: refactor to use gpui event instead of local state #18
@@ -1,3 +1,5 @@
|
|||||||
|
use std::cell::Cell;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ::settings::AppSettings;
|
use ::settings::AppSettings;
|
||||||
@@ -21,7 +23,7 @@ use ui::dock_area::panel::PanelView;
|
|||||||
use ui::dock_area::{ClosePanel, DockArea, DockItem};
|
use ui::dock_area::{ClosePanel, DockArea, DockItem};
|
||||||
use ui::menu::{DropdownMenu, PopupMenuItem};
|
use ui::menu::{DropdownMenu, PopupMenuItem};
|
||||||
use ui::notification::Notification;
|
use ui::notification::Notification;
|
||||||
use ui::{IconName, Root, Sizable, WindowExtension, h_flex, v_flex};
|
use ui::{Disableable, IconName, Root, Sizable, WindowExtension, h_flex, v_flex};
|
||||||
|
|
||||||
use crate::dialogs::{accounts, settings};
|
use crate::dialogs::{accounts, settings};
|
||||||
use crate::panels::{backup, contact_list, greeter, messaging_relays, profile, relay_list};
|
use crate::panels::{backup, contact_list, greeter, messaging_relays, profile, relay_list};
|
||||||
@@ -96,9 +98,39 @@ impl Workspace {
|
|||||||
subscriptions.push(
|
subscriptions.push(
|
||||||
// Subscribe to the signer events
|
// Subscribe to the signer events
|
||||||
cx.subscribe_in(&nostr, window, move |this, _state, event, window, cx| {
|
cx.subscribe_in(&nostr, window, move |this, _state, event, window, cx| {
|
||||||
if let StateEvent::SignerSet = event {
|
match event {
|
||||||
|
StateEvent::Connecting => {
|
||||||
|
let note = Notification::new()
|
||||||
|
.message("Connecting to the bootstrap relay...")
|
||||||
|
.title("Relays")
|
||||||
|
.icon(IconName::Relay);
|
||||||
|
|
||||||
|
window.push_notification(note, cx);
|
||||||
|
}
|
||||||
|
StateEvent::Connected => {
|
||||||
|
let note = Notification::new()
|
||||||
|
.message("Connected to the bootstrap relay")
|
||||||
|
.title("Relays")
|
||||||
|
.icon(IconName::Relay);
|
||||||
|
|
||||||
|
window.push_notification(note, cx);
|
||||||
|
}
|
||||||
|
StateEvent::RelayNotConfigured => {
|
||||||
|
this.relay_notification(window, cx);
|
||||||
|
}
|
||||||
|
StateEvent::RelayConnected => {
|
||||||
|
let note = Notification::new()
|
||||||
|
.message("Connected to user's relay list")
|
||||||
|
.title("Relays")
|
||||||
|
.icon(IconName::Relay);
|
||||||
|
|
||||||
|
window.push_notification(note, cx);
|
||||||
|
}
|
||||||
|
StateEvent::SignerSet => {
|
||||||
this.set_center_layout(window, cx);
|
this.set_center_layout(window, cx);
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -283,27 +315,16 @@ impl Workspace {
|
|||||||
this.get_announcement(cx);
|
this.get_announcement(cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Command::RefreshRelayList => {
|
|
||||||
let nostr = NostrRegistry::global(cx);
|
|
||||||
nostr.update(cx, |this, cx| {
|
|
||||||
//this.ensure_relay_list(cx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Command::ResetEncryption => {
|
Command::ResetEncryption => {
|
||||||
self.confirm_reset_encryption(window, cx);
|
self.confirm_reset_encryption(window, cx);
|
||||||
}
|
}
|
||||||
Command::RefreshMessagingRelays => {
|
|
||||||
let chat = ChatRegistry::global(cx);
|
|
||||||
chat.update(cx, |this, cx| {
|
|
||||||
//this.ensure_messaging_relays(cx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Command::ToggleTheme => {
|
Command::ToggleTheme => {
|
||||||
self.theme_selector(window, cx);
|
self.theme_selector(window, cx);
|
||||||
}
|
}
|
||||||
Command::ToggleAccount => {
|
Command::ToggleAccount => {
|
||||||
self.account_selector(window, cx);
|
self.account_selector(window, cx);
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,7 +471,61 @@ impl Workspace {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn titlebar_left(&mut self, _window: &mut Window, cx: &Context<Self>) -> impl IntoElement {
|
fn relay_notification(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
const BODY: &str = "Coop cannot found your gossip relay list. \
|
||||||
|
Maybe you haven't set it yet or relay not responsed";
|
||||||
|
|
||||||
|
let nostr = NostrRegistry::global(cx);
|
||||||
|
let signer = nostr.read(cx).signer();
|
||||||
|
|
||||||
|
let Some(public_key) = signer.public_key() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let entity = nostr.downgrade();
|
||||||
|
let loading = Rc::new(Cell::new(false));
|
||||||
|
|
||||||
|
let note = Notification::new()
|
||||||
|
.autohide(false)
|
||||||
|
.icon(IconName::Relay)
|
||||||
|
.title("Gossip Relays are required")
|
||||||
|
.content(move |_window, cx| {
|
||||||
|
v_flex()
|
||||||
|
.text_sm()
|
||||||
|
.text_color(cx.theme().text_muted)
|
||||||
|
.child(SharedString::from(BODY))
|
||||||
|
.into_any_element()
|
||||||
|
})
|
||||||
|
.action(move |_window, _cx| {
|
||||||
|
let entity = entity.clone();
|
||||||
|
let public_key = public_key.to_owned();
|
||||||
|
|
||||||
|
Button::new("retry")
|
||||||
|
.label("Retry")
|
||||||
|
.small()
|
||||||
|
.primary()
|
||||||
|
.loading(loading.get())
|
||||||
|
.disabled(loading.get())
|
||||||
|
.on_click({
|
||||||
|
let loading = Rc::clone(&loading);
|
||||||
|
|
||||||
|
move |_ev, _window, cx| {
|
||||||
|
// Set loading state to true
|
||||||
|
loading.set(true);
|
||||||
|
// Retry
|
||||||
|
entity
|
||||||
|
.update(cx, |this, cx| {
|
||||||
|
this.ensure_relay_list(&public_key, cx);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
window.push_notification(note, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn titlebar_left(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let nostr = NostrRegistry::global(cx);
|
let nostr = NostrRegistry::global(cx);
|
||||||
let signer = nostr.read(cx).signer();
|
let signer = nostr.read(cx).signer();
|
||||||
let current_user = signer.public_key();
|
let current_user = signer.public_key();
|
||||||
@@ -529,16 +604,7 @@ impl Workspace {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn titlebar_right(&mut self, _window: &mut Window, cx: &Context<Self>) -> impl IntoElement {
|
fn titlebar_right(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let nostr = NostrRegistry::global(cx);
|
|
||||||
let signer = nostr.read(cx).signer();
|
|
||||||
|
|
||||||
let chat = ChatRegistry::global(cx);
|
|
||||||
|
|
||||||
let Some(pkey) = signer.public_key() else {
|
|
||||||
return div();
|
|
||||||
};
|
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.when(!cx.theme().platform.is_mac(), |this| this.pr_2())
|
.when(!cx.theme().platform.is_mac(), |this| this.pr_2())
|
||||||
.gap_3()
|
.gap_3()
|
||||||
@@ -583,143 +649,6 @@ impl Workspace {
|
|||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
/*
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.gap_2()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.text_xs()
|
|
||||||
.text_color(cx.theme().text_muted)
|
|
||||||
.map(|this| match inbox_state {
|
|
||||||
InboxState::Checking => this.child(div().child(
|
|
||||||
SharedString::from("Fetching user's messaging relay list..."),
|
|
||||||
)),
|
|
||||||
InboxState::RelayNotAvailable => {
|
|
||||||
this.child(div().text_color(cx.theme().warning_active).child(
|
|
||||||
SharedString::from(
|
|
||||||
"User hasn't configured a messaging relay list",
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => this,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new("inbox")
|
|
||||||
.icon(IconName::Inbox)
|
|
||||||
.tooltip("Inbox")
|
|
||||||
.small()
|
|
||||||
.ghost()
|
|
||||||
.when(inbox_state.subscribing(), |this| this.indicator())
|
|
||||||
.dropdown_menu(move |this, _window, cx| {
|
|
||||||
let persons = PersonRegistry::global(cx);
|
|
||||||
let profile = persons.read(cx).get(&pkey, cx);
|
|
||||||
let urls: Vec<SharedString> = profile
|
|
||||||
.messaging_relays()
|
|
||||||
.iter()
|
|
||||||
.map(|url| SharedString::from(url.to_string()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Header
|
|
||||||
let menu = this.min_w(px(260.)).label("Messaging Relays");
|
|
||||||
|
|
||||||
// Content
|
|
||||||
let menu = urls.into_iter().fold(menu, |this, url| {
|
|
||||||
this.item(PopupMenuItem::element(move |_window, _cx| {
|
|
||||||
h_flex()
|
|
||||||
.px_1()
|
|
||||||
.w_full()
|
|
||||||
.gap_2()
|
|
||||||
.text_sm()
|
|
||||||
.child(
|
|
||||||
div().size_1p5().rounded_full().bg(gpui::green()),
|
|
||||||
)
|
|
||||||
.child(url.clone())
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
|
|
||||||
// Footer
|
|
||||||
menu.separator()
|
|
||||||
.menu_with_icon(
|
|
||||||
"Reload",
|
|
||||||
IconName::Refresh,
|
|
||||||
Box::new(Command::RefreshMessagingRelays),
|
|
||||||
)
|
|
||||||
.menu_with_icon(
|
|
||||||
"Update relays",
|
|
||||||
IconName::Settings,
|
|
||||||
Box::new(Command::ShowMessaging),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.gap_2()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.text_xs()
|
|
||||||
.text_color(cx.theme().text_muted)
|
|
||||||
.map(|this| match nostr.read(cx).relay_list_state {
|
|
||||||
RelayState::Checking => this
|
|
||||||
.child(div().child(SharedString::from(
|
|
||||||
"Fetching user's relay list...",
|
|
||||||
))),
|
|
||||||
RelayState::NotConfigured => {
|
|
||||||
this.child(div().text_color(cx.theme().warning_active).child(
|
|
||||||
SharedString::from("User hasn't configured a relay list"),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => this,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new("relay-list")
|
|
||||||
.icon(IconName::Relay)
|
|
||||||
.tooltip("User's relay list")
|
|
||||||
.small()
|
|
||||||
.ghost()
|
|
||||||
.when(nostr.read(cx).relay_list_state.configured(), |this| {
|
|
||||||
this.indicator()
|
|
||||||
})
|
|
||||||
.dropdown_menu(move |this, _window, cx| {
|
|
||||||
let nostr = NostrRegistry::global(cx);
|
|
||||||
let urls: Vec<SharedString> = vec![];
|
|
||||||
|
|
||||||
// Header
|
|
||||||
let menu = this.min_w(px(260.)).label("Relays");
|
|
||||||
|
|
||||||
// Content
|
|
||||||
let menu = urls.into_iter().fold(menu, |this, url| {
|
|
||||||
this.item(PopupMenuItem::element(move |_window, _cx| {
|
|
||||||
h_flex()
|
|
||||||
.px_1()
|
|
||||||
.w_full()
|
|
||||||
.gap_2()
|
|
||||||
.text_sm()
|
|
||||||
.child(
|
|
||||||
div().size_1p5().rounded_full().bg(gpui::green()),
|
|
||||||
)
|
|
||||||
.child(url.clone())
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
|
|
||||||
// Footer
|
|
||||||
menu.separator()
|
|
||||||
.menu_with_icon(
|
|
||||||
"Reload",
|
|
||||||
IconName::Refresh,
|
|
||||||
Box::new(Command::RefreshRelayList),
|
|
||||||
)
|
|
||||||
.menu_with_icon(
|
|
||||||
"Update relay list",
|
|
||||||
IconName::Settings,
|
|
||||||
Box::new(Command::ShowRelayList),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
) */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
|
|
||||||
use gpui::SharedString;
|
|
||||||
use nostr_sdk::prelude::*;
|
|
||||||
|
|
||||||
/// Gossip
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct Gossip {
|
|
||||||
relays: HashMap<PublicKey, HashSet<(RelayUrl, Option<RelayMetadata>)>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Gossip {
|
|
||||||
pub fn read_only_relays(&self, public_key: &PublicKey) -> Vec<SharedString> {
|
|
||||||
self.relays
|
|
||||||
.get(public_key)
|
|
||||||
.map(|relays| {
|
|
||||||
relays
|
|
||||||
.iter()
|
|
||||||
.map(|(url, _)| url.to_string().into())
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get read relays for a given public key
|
|
||||||
pub fn read_relays(&self, public_key: &PublicKey) -> Vec<RelayUrl> {
|
|
||||||
self.relays
|
|
||||||
.get(public_key)
|
|
||||||
.map(|relays| {
|
|
||||||
relays
|
|
||||||
.iter()
|
|
||||||
.filter_map(|(url, metadata)| {
|
|
||||||
if metadata.is_none() || metadata == &Some(RelayMetadata::Read) {
|
|
||||||
Some(url.to_owned())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get write relays for a given public key
|
|
||||||
pub fn write_relays(&self, public_key: &PublicKey) -> Vec<RelayUrl> {
|
|
||||||
self.relays
|
|
||||||
.get(public_key)
|
|
||||||
.map(|relays| {
|
|
||||||
relays
|
|
||||||
.iter()
|
|
||||||
.filter_map(|(url, metadata)| {
|
|
||||||
if metadata.is_none() || metadata == &Some(RelayMetadata::Write) {
|
|
||||||
Some(url.to_owned())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Insert gossip relays for a public key
|
|
||||||
pub fn insert_relays(&mut self, event: &Event) {
|
|
||||||
self.relays.entry(event.pubkey).or_default().extend(
|
|
||||||
event
|
|
||||||
.tags
|
|
||||||
.iter()
|
|
||||||
.filter_map(|tag| {
|
|
||||||
if let Some(TagStandard::RelayMetadata {
|
|
||||||
relay_url,
|
|
||||||
metadata,
|
|
||||||
}) = tag.clone().to_standardized()
|
|
||||||
{
|
|
||||||
Some((relay_url, metadata))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.take(3),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,14 +14,12 @@ use nostr_sdk::prelude::*;
|
|||||||
mod blossom;
|
mod blossom;
|
||||||
mod constants;
|
mod constants;
|
||||||
mod device;
|
mod device;
|
||||||
mod gossip;
|
|
||||||
mod nip05;
|
mod nip05;
|
||||||
mod signer;
|
mod signer;
|
||||||
|
|
||||||
pub use blossom::*;
|
pub use blossom::*;
|
||||||
pub use constants::*;
|
pub use constants::*;
|
||||||
pub use device::*;
|
pub use device::*;
|
||||||
pub use gossip::*;
|
|
||||||
pub use nip05::*;
|
pub use nip05::*;
|
||||||
pub use signer::*;
|
pub use signer::*;
|
||||||
|
|
||||||
@@ -52,6 +50,8 @@ pub enum StateEvent {
|
|||||||
Connected,
|
Connected,
|
||||||
/// User has not set up NIP-65 relays
|
/// User has not set up NIP-65 relays
|
||||||
RelayNotConfigured,
|
RelayNotConfigured,
|
||||||
|
/// Connected to NIP-65 relays
|
||||||
|
RelayConnected,
|
||||||
/// A new signer has been set
|
/// A new signer has been set
|
||||||
SignerSet,
|
SignerSet,
|
||||||
/// An error occurred
|
/// An error occurred
|
||||||
@@ -76,7 +76,7 @@ pub struct NostrRegistry {
|
|||||||
app_keys: Keys,
|
app_keys: Keys,
|
||||||
|
|
||||||
/// Tasks for asynchronous operations
|
/// Tasks for asynchronous operations
|
||||||
tasks: Vec<Task<Result<(), Error>>>,
|
tasks: Vec<Task<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventEmitter<StateEvent> for NostrRegistry {}
|
impl EventEmitter<StateEvent> for NostrRegistry {}
|
||||||
@@ -173,34 +173,41 @@ impl NostrRegistry {
|
|||||||
fn connect(&mut self, cx: &mut Context<Self>) {
|
fn connect(&mut self, cx: &mut Context<Self>) {
|
||||||
let client = self.client();
|
let client = self.client();
|
||||||
|
|
||||||
// Emit connecting event
|
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
|
||||||
cx.emit(StateEvent::Connecting);
|
|
||||||
|
|
||||||
self.tasks.push(cx.spawn(async move |this, cx| {
|
|
||||||
cx.background_executor()
|
|
||||||
.await_on_background(async move {
|
|
||||||
// Add search relay to the relay pool
|
// Add search relay to the relay pool
|
||||||
for url in SEARCH_RELAYS.into_iter() {
|
for url in SEARCH_RELAYS.into_iter() {
|
||||||
client.add_relay(url).await.ok();
|
client.add_relay(url).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add bootstrap relay to the relay pool
|
// Add bootstrap relay to the relay pool
|
||||||
for url in BOOTSTRAP_RELAYS.into_iter() {
|
for url in BOOTSTRAP_RELAYS.into_iter() {
|
||||||
client.add_relay(url).await.ok();
|
client.add_relay(url).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to all added relays
|
// Connect to all added relays
|
||||||
client.connect().and_wait(Duration::from_secs(2)).await;
|
client.connect().and_wait(Duration::from_secs(2)).await;
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// Update the state
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
// Emit connecting event
|
||||||
|
cx.emit(StateEvent::Connecting);
|
||||||
|
|
||||||
|
self.tasks
|
||||||
|
.push(cx.spawn(async move |this, cx| match task.await {
|
||||||
|
Ok(_) => {
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
cx.emit(StateEvent::Connected);
|
cx.emit(StateEvent::Connected);
|
||||||
this.get_npubs(cx);
|
this.get_npubs(cx);
|
||||||
})?;
|
})
|
||||||
|
.ok();
|
||||||
Ok(())
|
}
|
||||||
|
Err(e) => {
|
||||||
|
this.update(cx, |_this, cx| {
|
||||||
|
cx.emit(StateEvent::Error(SharedString::from(e.to_string())));
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,24 +254,26 @@ impl NostrRegistry {
|
|||||||
true => {
|
true => {
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
this.create_identity(cx);
|
this.create_identity(cx);
|
||||||
})?;
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
false => {
|
false => {
|
||||||
// TODO: auto login
|
// TODO: auto login
|
||||||
npubs.update(cx, |this, cx| {
|
npubs
|
||||||
|
.update(cx, |this, cx| {
|
||||||
this.extend(public_keys);
|
this.extend(public_keys);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})?;
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
this.update(cx, |_this, cx| {
|
this.update(cx, |_this, cx| {
|
||||||
cx.emit(StateEvent::Error(SharedString::from(e.to_string())));
|
cx.emit(StateEvent::Error(SharedString::from(e.to_string())));
|
||||||
})?;
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,15 +346,20 @@ impl NostrRegistry {
|
|||||||
});
|
});
|
||||||
|
|
||||||
self.tasks.push(cx.spawn(async move |this, cx| {
|
self.tasks.push(cx.spawn(async move |this, cx| {
|
||||||
// Wait for the task to complete
|
match task.await {
|
||||||
task.await?;
|
Ok(_) => {
|
||||||
|
|
||||||
// Set signer
|
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
this.set_signer(keys, cx);
|
this.set_signer(keys, cx);
|
||||||
})?;
|
})
|
||||||
|
.ok();
|
||||||
Ok(())
|
}
|
||||||
|
Err(e) => {
|
||||||
|
this.update(cx, |_this, cx| {
|
||||||
|
cx.emit(StateEvent::Error(SharedString::from(e.to_string())));
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,6 +438,7 @@ impl NostrRegistry {
|
|||||||
Ok(public_key) => {
|
Ok(public_key) => {
|
||||||
// Update states
|
// Update states
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
|
this.ensure_relay_list(&public_key, cx);
|
||||||
// Add public key to npubs if not already present
|
// Add public key to npubs if not already present
|
||||||
this.npubs.update(cx, |this, cx| {
|
this.npubs.update(cx, |this, cx| {
|
||||||
if !this.contains(&public_key) {
|
if !this.contains(&public_key) {
|
||||||
@@ -433,16 +448,16 @@ impl NostrRegistry {
|
|||||||
});
|
});
|
||||||
// Emit signer changed event
|
// Emit signer changed event
|
||||||
cx.emit(StateEvent::SignerSet);
|
cx.emit(StateEvent::SignerSet);
|
||||||
})?;
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
this.update(cx, |_this, cx| {
|
this.update(cx, |_this, cx| {
|
||||||
cx.emit(StateEvent::Error(SharedString::from(e.to_string())));
|
cx.emit(StateEvent::Error(SharedString::from(e.to_string())));
|
||||||
})?;
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,16 +469,15 @@ impl NostrRegistry {
|
|||||||
|
|
||||||
self.tasks.push(cx.spawn(async move |this, cx| {
|
self.tasks.push(cx.spawn(async move |this, cx| {
|
||||||
let key_path = keys_dir.join(format!("{}.npub", npub));
|
let key_path = keys_dir.join(format!("{}.npub", npub));
|
||||||
smol::fs::remove_file(key_path).await?;
|
smol::fs::remove_file(key_path).await.ok();
|
||||||
|
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
this.npubs().update(cx, |this, cx| {
|
this.npubs().update(cx, |this, cx| {
|
||||||
this.retain(|k| k != &public_key);
|
this.retain(|k| k != &public_key);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
})?;
|
})
|
||||||
|
.ok();
|
||||||
Ok(())
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,16 +495,16 @@ impl NostrRegistry {
|
|||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
this.set_signer(keys, cx);
|
this.set_signer(keys, cx);
|
||||||
})?;
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
this.update(cx, |_this, cx| {
|
this.update(cx, |_this, cx| {
|
||||||
cx.emit(StateEvent::Error(SharedString::from(e.to_string())));
|
cx.emit(StateEvent::Error(SharedString::from(e.to_string())));
|
||||||
})?;
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,32 +526,88 @@ impl NostrRegistry {
|
|||||||
match task.await {
|
match task.await {
|
||||||
Ok((public_key, uri)) => {
|
Ok((public_key, uri)) => {
|
||||||
let username = public_key.to_bech32().unwrap();
|
let username = public_key.to_bech32().unwrap();
|
||||||
let write_credential = this.read_with(cx, |_this, cx| {
|
let write_credential = this
|
||||||
cx.write_credentials(&username, "nostrconnect", uri.to_string().as_bytes())
|
.read_with(cx, |_this, cx| {
|
||||||
})?;
|
cx.write_credentials(
|
||||||
|
&username,
|
||||||
|
"nostrconnect",
|
||||||
|
uri.to_string().as_bytes(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
match write_credential.await {
|
match write_credential.await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
this.set_signer(nip46, cx);
|
this.set_signer(nip46, cx);
|
||||||
})?;
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
this.update(cx, |_this, cx| {
|
this.update(cx, |_this, cx| {
|
||||||
cx.emit(StateEvent::Error(SharedString::from(e.to_string())));
|
cx.emit(StateEvent::Error(SharedString::from(e.to_string())));
|
||||||
})?;
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
this.update(cx, |_this, cx| {
|
this.update(cx, |_this, cx| {
|
||||||
cx.emit(StateEvent::Error(SharedString::from(e.to_string())));
|
cx.emit(StateEvent::Error(SharedString::from(e.to_string())));
|
||||||
})?;
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure_relay_list(&mut self, public_key: &PublicKey, cx: &mut Context<Self>) {
|
||||||
|
let task = self.get_event(public_key, Kind::RelayList, cx);
|
||||||
|
|
||||||
|
self.tasks.push(cx.spawn(async move |this, cx| {
|
||||||
|
match task.await {
|
||||||
|
Ok(_) => {
|
||||||
|
this.update(cx, |_this, cx| {
|
||||||
|
cx.emit(StateEvent::RelayConnected);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
this.update(cx, |_this, cx| {
|
||||||
|
cx.emit(StateEvent::RelayNotConfigured);
|
||||||
|
cx.emit(StateEvent::Error(SharedString::from(e.to_string())));
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an event with the given author and kind.
|
||||||
|
pub fn get_event(
|
||||||
|
&self,
|
||||||
|
author: &PublicKey,
|
||||||
|
kind: Kind,
|
||||||
|
cx: &App,
|
||||||
|
) -> Task<Result<Event, Error>> {
|
||||||
|
let client = self.client();
|
||||||
|
let public_key = *author;
|
||||||
|
|
||||||
|
cx.background_spawn(async move {
|
||||||
|
let filter = Filter::new().kind(kind).author(public_key).limit(1);
|
||||||
|
let mut stream = client
|
||||||
|
.stream_events(filter)
|
||||||
|
.timeout(Duration::from_millis(800))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
while let Some((_url, res)) = stream.next().await {
|
||||||
|
if let Ok(event) = res {
|
||||||
|
return Ok(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Err(anyhow!("No event found"))
|
||||||
}));
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the public key of a NIP-05 address
|
/// Get the public key of a NIP-05 address
|
||||||
|
|||||||
Reference in New Issue
Block a user