chore: internal changes
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use asset::Assets;
|
||||
use chats::registry::ChatRegistry;
|
||||
use common::constants::{
|
||||
ALL_MESSAGES_SUB_ID, APP_ID, APP_NAME, KEYRING_SERVICE, NEW_MESSAGE_SUB_ID,
|
||||
ALL_MESSAGES_SUB_ID, APP_ID, APP_NAME, BOOTSTRAP_RELAYS, KEYRING_SERVICE, NEW_MESSAGE_SUB_ID,
|
||||
};
|
||||
use futures::{select, FutureExt};
|
||||
use gpui::{
|
||||
@@ -12,11 +12,9 @@ use gpui::{
|
||||
use gpui::{point, SharedString, TitlebarOptions};
|
||||
#[cfg(target_os = "linux")]
|
||||
use gpui::{WindowBackgroundAppearance, WindowDecorations};
|
||||
use log::{error, info};
|
||||
use nostr_sdk::SubscriptionId;
|
||||
use nostr_sdk::{
|
||||
pool::prelude::ReqExitPolicy, Client, Event, Filter, Keys, Kind, PublicKey, RelayMessage,
|
||||
RelayPoolNotification, SubscribeAutoCloseOptions,
|
||||
RelayPoolNotification, SubscribeAutoCloseOptions, SubscriptionId,
|
||||
};
|
||||
use smol::Timer;
|
||||
use state::get_client;
|
||||
@@ -58,11 +56,11 @@ fn main() {
|
||||
// Connect to default relays
|
||||
app.background_executor()
|
||||
.spawn(async {
|
||||
_ = client.add_relay("wss://relay.damus.io/").await;
|
||||
_ = client.add_relay("wss://relay.primal.net/").await;
|
||||
_ = client.add_relay("wss://user.kindpag.es/").await;
|
||||
_ = client.add_relay("wss://purplepag.es/").await;
|
||||
for relay in BOOTSTRAP_RELAYS.iter() {
|
||||
_ = client.add_relay(*relay).await;
|
||||
}
|
||||
_ = client.add_discovery_relay("wss://relaydiscovery.com").await;
|
||||
_ = client.add_discovery_relay("wss://user.kindpag.es").await;
|
||||
_ = client.connect().await
|
||||
})
|
||||
.detach();
|
||||
@@ -131,12 +129,15 @@ fn main() {
|
||||
if let Err(e) =
|
||||
client.database().save_event(&event).await
|
||||
{
|
||||
error!("Failed to save event: {}", e);
|
||||
log::error!("Failed to save event: {}", e);
|
||||
}
|
||||
|
||||
// Send all pubkeys to the batch
|
||||
if let Err(e) = batch_tx.send(pubkeys).await {
|
||||
error!("Failed to send pubkeys to batch: {}", e)
|
||||
log::error!(
|
||||
"Failed to send pubkeys to batch: {}",
|
||||
e
|
||||
)
|
||||
}
|
||||
|
||||
// Send this event to the GPUI
|
||||
@@ -144,7 +145,10 @@ fn main() {
|
||||
if let Err(e) =
|
||||
event_tx.send(Signal::Event(event)).await
|
||||
{
|
||||
error!("Failed to send event to GPUI: {}", e)
|
||||
log::error!(
|
||||
"Failed to send event to GPUI: {}",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,6 +157,7 @@ fn main() {
|
||||
Kind::ContactList => {
|
||||
let pubkeys =
|
||||
event.tags.public_keys().copied().collect::<HashSet<_>>();
|
||||
|
||||
sync_metadata(client, pubkeys).await;
|
||||
}
|
||||
_ => {}
|
||||
@@ -161,7 +166,7 @@ fn main() {
|
||||
RelayMessage::EndOfStoredEvents(subscription_id) => {
|
||||
if all_id == *subscription_id {
|
||||
if let Err(e) = event_tx.send(Signal::Eose).await {
|
||||
error!("Failed to send eose: {}", e)
|
||||
log::error!("Failed to send eose: {}", e)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -296,14 +301,17 @@ fn main() {
|
||||
}
|
||||
|
||||
async fn sync_metadata(client: &Client, buffer: HashSet<PublicKey>) {
|
||||
let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE);
|
||||
let opts = SubscribeAutoCloseOptions::default()
|
||||
.exit_policy(ReqExitPolicy::ExitOnEOSE)
|
||||
.idle_timeout(Some(Duration::from_secs(2)));
|
||||
|
||||
let filter = Filter::new()
|
||||
.authors(buffer.iter().cloned())
|
||||
.kind(Kind::Metadata)
|
||||
.limit(buffer.len());
|
||||
|
||||
if let Err(e) = client.subscribe(filter, Some(opts)).await {
|
||||
error!("Subscribe error: {e}");
|
||||
log::error!("Failed to sync metadata: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,6 +370,6 @@ async fn restore_window(is_login: bool, cx: &mut AsyncApp) -> anyhow::Result<()>
|
||||
}
|
||||
|
||||
fn quit(_: &Quit, cx: &mut App) {
|
||||
info!("Gracefully quitting the application . . .");
|
||||
log::info!("Gracefully quitting the application . . .");
|
||||
cx.quit();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ use gpui::{
|
||||
Context, Entity, InteractiveElement, IntoElement, ObjectFit, ParentElement, Render, Styled,
|
||||
StyledImage, Window,
|
||||
};
|
||||
use nostr_sdk::prelude::*;
|
||||
use serde::Deserialize;
|
||||
use state::get_client;
|
||||
use std::sync::Arc;
|
||||
@@ -96,56 +95,27 @@ impl AppView {
|
||||
}
|
||||
|
||||
fn verify_user_relays(&self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let Some(account) = Account::global(cx) else {
|
||||
let Some(model) = Account::global(cx) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let public_key = account.read(cx).get().public_key();
|
||||
let client = get_client();
|
||||
let account = model.read(cx);
|
||||
let task = account.verify_inbox_relays(cx);
|
||||
let window_handle = window.window_handle();
|
||||
let (tx, rx) = oneshot::channel::<Option<Vec<String>>>();
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let filter = Filter::new()
|
||||
.kind(Kind::InboxRelays)
|
||||
.author(public_key)
|
||||
.limit(1);
|
||||
|
||||
let relays = client
|
||||
.database()
|
||||
.query(filter)
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|events| events.first_owned())
|
||||
.map(|event| {
|
||||
event
|
||||
.tags
|
||||
.filter_standardized(TagKind::Relay)
|
||||
.filter_map(|t| match t {
|
||||
TagStandard::Relay(url) => Some(url.to_string()),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
_ = tx.send(relays);
|
||||
})
|
||||
.detach();
|
||||
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
if let Ok(Some(relays)) = rx.await {
|
||||
if let Ok(relays) = task.await {
|
||||
_ = cx.update(|cx| {
|
||||
_ = this.update(cx, |this, cx| {
|
||||
let relays = cx.new(|_| Some(relays));
|
||||
this.relays = relays;
|
||||
this.relays = cx.new(|_| Some(relays));
|
||||
cx.notify();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
_ = cx.update_window(window_handle, |_, window, cx| {
|
||||
this.update(cx, |this: &mut Self, cx| {
|
||||
_ = this.update(cx, |this: &mut Self, cx| {
|
||||
this.render_setup_relays(window, cx)
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
@@ -29,7 +29,8 @@ use ui::{
|
||||
v_flex, ContextModal, Icon, IconName, Sizable, StyledExt,
|
||||
};
|
||||
|
||||
const ALERT: &str =
|
||||
const ALERT: &str = "has not set up Messaging (DM) Relays, so they will NOT receive your messages.";
|
||||
const DESCRIPTION: &str =
|
||||
"This conversation is private. Only members of this chat can see each other's messages.";
|
||||
|
||||
pub fn init(
|
||||
@@ -186,55 +187,25 @@ impl Chat {
|
||||
return;
|
||||
};
|
||||
|
||||
let client = get_client();
|
||||
let (tx, rx) = oneshot::channel::<Vec<(PublicKey, bool)>>();
|
||||
|
||||
let pubkeys: Vec<PublicKey> = model
|
||||
.read(cx)
|
||||
.members
|
||||
.iter()
|
||||
.map(|m| m.public_key())
|
||||
.collect();
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let mut result = Vec::new();
|
||||
|
||||
for pubkey in pubkeys.into_iter() {
|
||||
let filter = Filter::new()
|
||||
.kind(Kind::InboxRelays)
|
||||
.author(pubkey)
|
||||
.limit(1);
|
||||
|
||||
let is_ready = if let Ok(events) = client.database().query(filter).await {
|
||||
events.first_owned().is_some()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
result.push((pubkey, is_ready));
|
||||
}
|
||||
|
||||
_ = tx.send(result);
|
||||
})
|
||||
.detach();
|
||||
let room = model.read(cx);
|
||||
let task = room.verify_inbox_relays(cx);
|
||||
|
||||
cx.spawn(|this, cx| async move {
|
||||
if let Ok(result) = rx.await {
|
||||
if let Ok(result) = task.await {
|
||||
_ = cx.update(|cx| {
|
||||
_ = this.update(cx, |this, cx| {
|
||||
for item in result.into_iter() {
|
||||
result.into_iter().for_each(|item| {
|
||||
if !item.1 {
|
||||
let name = this
|
||||
.room
|
||||
.read_with(cx, |this, _| this.name().unwrap_or("Unnamed".into()))
|
||||
.unwrap_or("Unnamed".into());
|
||||
|
||||
this.push_system_message(
|
||||
format!("{} has not set up Messaging (DM) Relays, so they will NOT receive your messages.", name),
|
||||
cx,
|
||||
);
|
||||
if let Ok(Some(member)) =
|
||||
this.room.read_with(cx, |this, _| this.member(&item.0))
|
||||
{
|
||||
this.push_system_message(
|
||||
format!("{} {}", ALERT, member.name()),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -549,7 +520,7 @@ impl Chat {
|
||||
.group_hover("", |this| this.bg(cx.theme().danger)),
|
||||
)
|
||||
.child(
|
||||
img("brand/avatar.png")
|
||||
img("brand/avatar.jpg")
|
||||
.size_8()
|
||||
.rounded_full()
|
||||
.flex_shrink_0(),
|
||||
@@ -574,7 +545,7 @@ impl Chat {
|
||||
.size_8()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::THREE)),
|
||||
)
|
||||
.child(ALERT),
|
||||
.child(DESCRIPTION),
|
||||
})
|
||||
} else {
|
||||
div()
|
||||
|
||||
@@ -49,7 +49,7 @@ impl Profile {
|
||||
TextInput::new(window, cx)
|
||||
.text_size(Size::XSmall)
|
||||
.small()
|
||||
.placeholder("https://example.com/avatar.png")
|
||||
.placeholder("https://example.com/avatar.jpg")
|
||||
});
|
||||
|
||||
let website_input = cx.new(|cx| {
|
||||
@@ -309,7 +309,7 @@ impl Render for Profile {
|
||||
|
||||
if picture.is_empty() {
|
||||
this.child(
|
||||
img("brand/avatar.png")
|
||||
img("brand/avatar.jpg")
|
||||
.size_10()
|
||||
.rounded_full()
|
||||
.flex_shrink_0(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, px, uniform_list, AppContext, Context, Entity, FocusHandle,
|
||||
InteractiveElement, IntoElement, ParentElement, Render, Styled, TextAlign, Window,
|
||||
InteractiveElement, IntoElement, ParentElement, Render, Styled, Task, TextAlign, Window,
|
||||
};
|
||||
use nostr_sdk::prelude::*;
|
||||
use state::get_client;
|
||||
@@ -63,31 +63,23 @@ impl Relays {
|
||||
let relays = self.relays.read(cx).clone();
|
||||
let window_handle = window.window_handle();
|
||||
|
||||
// Show loading spinner
|
||||
self.set_loading(true, cx);
|
||||
|
||||
let client = get_client();
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let signer = client.signer().await.expect("Signer is required");
|
||||
let public_key = signer
|
||||
.get_public_key()
|
||||
.await
|
||||
.expect("Cannot get public key");
|
||||
let task: Task<Result<EventId, anyhow::Error>> = cx.background_spawn(async move {
|
||||
let client = get_client();
|
||||
let signer = client.signer().await?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
|
||||
// If user didn't have any NIP-65 relays, add default ones
|
||||
// TODO: Is this really necessary?
|
||||
if let Ok(relay_list) = client.database().relay_list(public_key).await {
|
||||
if relay_list.is_empty() {
|
||||
let builder = EventBuilder::relay_list(vec![
|
||||
(RelayUrl::parse("wss://relay.damus.io/").unwrap(), None),
|
||||
(RelayUrl::parse("wss://relay.primal.net/").unwrap(), None),
|
||||
(RelayUrl::parse("wss://nos.lol/").unwrap(), None),
|
||||
]);
|
||||
if client.database().relay_list(public_key).await?.is_empty() {
|
||||
let builder = EventBuilder::relay_list(vec![
|
||||
(RelayUrl::parse("wss://relay.damus.io/").unwrap(), None),
|
||||
(RelayUrl::parse("wss://relay.primal.net/").unwrap(), None),
|
||||
]);
|
||||
|
||||
if let Err(e) = client.send_event_builder(builder).await {
|
||||
log::error!("Failed to send relay list event: {}", e)
|
||||
}
|
||||
if let Err(e) = client.send_event_builder(builder).await {
|
||||
log::error!("Failed to send relay list event: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,15 +89,13 @@ impl Relays {
|
||||
.collect();
|
||||
|
||||
let builder = EventBuilder::new(Kind::InboxRelays, "").tags(tags);
|
||||
let output = client.send_event_builder(builder).await?;
|
||||
|
||||
if let Ok(output) = client.send_event_builder(builder).await {
|
||||
_ = tx.send(output.val);
|
||||
};
|
||||
})
|
||||
.detach();
|
||||
Ok(output.val)
|
||||
});
|
||||
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
if rx.await.is_ok() {
|
||||
if task.await.is_ok() {
|
||||
_ = cx.update_window(window_handle, |_, window, cx| {
|
||||
_ = this.update(cx, |this, cx| {
|
||||
this.set_loading(false, cx);
|
||||
|
||||
@@ -3,10 +3,12 @@ use common::{profile::NostrProfile, utils::random_name};
|
||||
use gpui::{
|
||||
div, img, impl_internal_actions, prelude::FluentBuilder, px, relative, uniform_list, App,
|
||||
AppContext, Context, Entity, FocusHandle, InteractiveElement, IntoElement, ParentElement,
|
||||
Render, SharedString, StatefulInteractiveElement, Styled, Subscription, TextAlign, Window,
|
||||
Render, SharedString, StatefulInteractiveElement, Styled, Subscription, Task, TextAlign,
|
||||
Window,
|
||||
};
|
||||
use nostr_sdk::prelude::*;
|
||||
use serde::Deserialize;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use smol::Timer;
|
||||
use state::get_client;
|
||||
use std::{collections::HashSet, time::Duration};
|
||||
@@ -17,7 +19,7 @@ use ui::{
|
||||
ContextModal, Icon, IconName, Sizable, Size, StyledExt,
|
||||
};
|
||||
|
||||
const ALERT: &str =
|
||||
const DESCRIPTION: &str =
|
||||
"Start a conversation with someone using their npub or NIP-05 (like foo@bar.com).";
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Deserialize)]
|
||||
@@ -35,7 +37,7 @@ pub struct Compose {
|
||||
is_submitting: bool,
|
||||
error_message: Entity<Option<SharedString>>,
|
||||
#[allow(dead_code)]
|
||||
subscriptions: Vec<Subscription>,
|
||||
subscriptions: SmallVec<[Subscription; 1]>,
|
||||
}
|
||||
|
||||
impl Compose {
|
||||
@@ -43,7 +45,6 @@ impl Compose {
|
||||
let contacts = cx.new(|_| Vec::new());
|
||||
let selected = cx.new(|_| HashSet::new());
|
||||
let error_message = cx.new(|_| None);
|
||||
let mut subscriptions = Vec::new();
|
||||
|
||||
let title_input = cx.new(|cx| {
|
||||
let name = random_name(2);
|
||||
@@ -63,17 +64,15 @@ impl Compose {
|
||||
.placeholder("npub1...")
|
||||
});
|
||||
|
||||
let mut subscriptions = smallvec![];
|
||||
|
||||
// Handle Enter event for user input
|
||||
subscriptions.push(cx.subscribe_in(
|
||||
&user_input,
|
||||
window,
|
||||
move |this, input, input_event, window, cx| {
|
||||
move |this, _, input_event, window, cx| {
|
||||
if let InputEvent::PressEnter = input_event {
|
||||
if input.read(cx).text().contains("@") {
|
||||
this.add_nip05(window, cx);
|
||||
} else {
|
||||
this.add_npub(window, cx)
|
||||
}
|
||||
this.add(window, cx);
|
||||
}
|
||||
},
|
||||
));
|
||||
@@ -147,50 +146,48 @@ impl Compose {
|
||||
}
|
||||
|
||||
let tags = Tags::from_list(tag_list);
|
||||
let client = get_client();
|
||||
let window_handle = window.window_handle();
|
||||
let (tx, rx) = oneshot::channel::<Event>();
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let signer = client.signer().await.expect("Signer is required");
|
||||
let event: Task<Result<Event, anyhow::Error>> = cx.background_spawn(async move {
|
||||
let client = get_client();
|
||||
let signer = client.signer().await?;
|
||||
// [IMPORTANT]
|
||||
// Make sure this event is never send,
|
||||
// this event existed just use for convert to Coop's Chat Room later.
|
||||
if let Ok(event) = EventBuilder::private_msg_rumor(*pubkeys.last().unwrap(), "")
|
||||
let event = EventBuilder::private_msg_rumor(*pubkeys.last().unwrap(), "")
|
||||
.tags(tags)
|
||||
.sign(&signer)
|
||||
.await
|
||||
{
|
||||
_ = tx.send(event)
|
||||
};
|
||||
})
|
||||
.detach();
|
||||
.await?;
|
||||
|
||||
Ok(event)
|
||||
});
|
||||
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
if let Ok(event) = rx.await {
|
||||
if let Ok(event) = event.await {
|
||||
_ = cx.update_window(window_handle, |_, window, cx| {
|
||||
// Stop loading spinner
|
||||
_ = this.update(cx, |this, cx| {
|
||||
this.set_submitting(false, cx);
|
||||
});
|
||||
|
||||
if let Some(chats) = ChatRegistry::global(cx) {
|
||||
let room = Room::new(&event, cx);
|
||||
let Some(chats) = ChatRegistry::global(cx) else {
|
||||
return;
|
||||
};
|
||||
let room = Room::new(&event, cx);
|
||||
|
||||
chats.update(cx, |state, cx| {
|
||||
match state.push_room(room, cx) {
|
||||
Ok(_) => {
|
||||
// TODO: open chat panel
|
||||
window.close_modal(cx);
|
||||
}
|
||||
Err(e) => {
|
||||
_ = this.update(cx, |this, cx| {
|
||||
this.set_error(Some(e.to_string().into()), cx);
|
||||
});
|
||||
}
|
||||
chats.update(cx, |state, cx| {
|
||||
match state.push_room(room, cx) {
|
||||
Ok(_) => {
|
||||
// TODO: automatically open newly created chat panel
|
||||
window.close_modal(cx);
|
||||
}
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
_ = this.update(cx, |this, cx| {
|
||||
this.set_error(Some(e.to_string().into()), cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
@@ -209,128 +206,77 @@ impl Compose {
|
||||
self.is_submitting
|
||||
}
|
||||
|
||||
fn add_npub(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
fn add(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let client = get_client();
|
||||
let window_handle = window.window_handle();
|
||||
let content = self.user_input.read(cx).text().to_string();
|
||||
|
||||
// Show loading spinner
|
||||
self.set_loading(true, cx);
|
||||
|
||||
let Ok(public_key) = PublicKey::parse(&content) else {
|
||||
self.set_loading(false, cx);
|
||||
self.set_error(Some("Public Key is not valid".into()), cx);
|
||||
return;
|
||||
let task: Task<Result<NostrProfile, anyhow::Error>> = if content.starts_with("npub1") {
|
||||
let Ok(public_key) = PublicKey::parse(&content) else {
|
||||
self.set_loading(false, cx);
|
||||
self.set_error(Some("Public Key is not valid".into()), cx);
|
||||
return;
|
||||
};
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let metadata = client
|
||||
.fetch_metadata(public_key, Duration::from_secs(2))
|
||||
.await?;
|
||||
|
||||
Ok(NostrProfile::new(public_key, metadata))
|
||||
})
|
||||
} else {
|
||||
cx.background_spawn(async move {
|
||||
let profile = nip05::profile(&content, None).await?;
|
||||
let public_key = profile.public_key;
|
||||
|
||||
let metadata = client
|
||||
.fetch_metadata(public_key, Duration::from_secs(2))
|
||||
.await?;
|
||||
|
||||
Ok(NostrProfile::new(public_key, metadata))
|
||||
})
|
||||
};
|
||||
|
||||
if self
|
||||
.contacts
|
||||
.read(cx)
|
||||
.iter()
|
||||
.any(|c| c.public_key() == public_key)
|
||||
{
|
||||
self.set_loading(false, cx);
|
||||
return;
|
||||
};
|
||||
|
||||
let client = get_client();
|
||||
let (tx, rx) = oneshot::channel::<Metadata>();
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let metadata = (client
|
||||
.fetch_metadata(public_key, Duration::from_secs(2))
|
||||
.await)
|
||||
.unwrap_or_default();
|
||||
|
||||
_ = tx.send(metadata);
|
||||
})
|
||||
.detach();
|
||||
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
if let Ok(metadata) = rx.await {
|
||||
_ = cx.update_window(window_handle, |_, window, cx| {
|
||||
_ = this.update(cx, |this, cx| {
|
||||
this.contacts.update(cx, |this, cx| {
|
||||
this.insert(0, NostrProfile::new(public_key, metadata));
|
||||
cx.notify();
|
||||
});
|
||||
match task.await {
|
||||
Ok(profile) => {
|
||||
_ = cx.update_window(window_handle, |_, window, cx| {
|
||||
_ = this.update(cx, |this, cx| {
|
||||
let public_key = profile.public_key();
|
||||
|
||||
this.selected.update(cx, |this, cx| {
|
||||
this.insert(public_key);
|
||||
cx.notify();
|
||||
});
|
||||
this.contacts.update(cx, |this, cx| {
|
||||
this.insert(0, profile);
|
||||
cx.notify();
|
||||
});
|
||||
|
||||
// Stop loading indicator
|
||||
this.set_loading(false, cx);
|
||||
this.selected.update(cx, |this, cx| {
|
||||
this.insert(public_key);
|
||||
cx.notify();
|
||||
});
|
||||
|
||||
// Clear input
|
||||
this.user_input.update(cx, |this, cx| {
|
||||
this.set_text("", window, cx);
|
||||
cx.notify();
|
||||
// Stop loading indicator
|
||||
this.set_loading(false, cx);
|
||||
|
||||
// Clear input
|
||||
this.user_input.update(cx, |this, cx| {
|
||||
this.set_text("", window, cx);
|
||||
cx.notify();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn add_nip05(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let window_handle = window.window_handle();
|
||||
let content = self.user_input.read(cx).text().to_string();
|
||||
|
||||
// Show loading spinner
|
||||
self.set_loading(true, cx);
|
||||
|
||||
let client = get_client();
|
||||
let (tx, rx) = oneshot::channel::<Option<NostrProfile>>();
|
||||
|
||||
cx.background_spawn(async move {
|
||||
if let Ok(profile) = nip05::profile(&content, None).await {
|
||||
let metadata = (client
|
||||
.fetch_metadata(profile.public_key, Duration::from_secs(2))
|
||||
.await)
|
||||
.unwrap_or_default();
|
||||
|
||||
_ = tx.send(Some(NostrProfile::new(profile.public_key, metadata)));
|
||||
} else {
|
||||
_ = tx.send(None);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
if let Ok(Some(profile)) = rx.await {
|
||||
_ = cx.update_window(window_handle, |_, window, cx| {
|
||||
_ = this.update(cx, |this, cx| {
|
||||
let public_key = profile.public_key();
|
||||
|
||||
this.contacts.update(cx, |this, cx| {
|
||||
this.insert(0, profile);
|
||||
cx.notify();
|
||||
});
|
||||
|
||||
this.selected.update(cx, |this, cx| {
|
||||
this.insert(public_key);
|
||||
cx.notify();
|
||||
});
|
||||
|
||||
// Stop loading indicator
|
||||
this.set_loading(false, cx);
|
||||
|
||||
// Clear input
|
||||
this.user_input.update(cx, |this, cx| {
|
||||
this.set_text("", window, cx);
|
||||
cx.notify();
|
||||
}
|
||||
Err(e) => {
|
||||
_ = cx.update_window(window_handle, |_, _, cx| {
|
||||
_ = this.update(cx, |this, cx| {
|
||||
this.set_loading(false, cx);
|
||||
this.set_error(Some(e.to_string().into()), cx);
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
_ = cx.update_window(window_handle, |_, _, cx| {
|
||||
_ = this.update(cx, |this, cx| {
|
||||
this.set_loading(false, cx);
|
||||
this.set_error(Some("NIP-05 Address is not valid".into()), cx);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
@@ -395,7 +341,7 @@ impl Render for Compose {
|
||||
.px_2()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.child(ALERT),
|
||||
.child(DESCRIPTION),
|
||||
)
|
||||
.when_some(self.error_message.read(cx).as_ref(), |this, msg| {
|
||||
this.child(
|
||||
@@ -439,11 +385,7 @@ impl Render for Compose {
|
||||
.rounded(ButtonRounded::Size(px(9999.)))
|
||||
.loading(self.is_loading)
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
if this.user_input.read(cx).text().contains("@") {
|
||||
this.add_nip05(window, cx);
|
||||
} else {
|
||||
this.add_npub(window, cx);
|
||||
}
|
||||
this.add(window, cx);
|
||||
})),
|
||||
)
|
||||
.child(self.user_input.clone()),
|
||||
|
||||
Reference in New Issue
Block a user