chore: improve nostr connect (#21)
* ref * update * temporary switch to rust-nostr fork * use nip46 branch
This commit is contained in:
@@ -38,13 +38,10 @@ impl Account {
|
||||
{
|
||||
let task: Task<Result<Profile, Error>> = cx.background_spawn(async move {
|
||||
let client = get_client();
|
||||
// Use user's signer for main signer
|
||||
_ = client.set_signer(signer).await;
|
||||
|
||||
// Verify nostr signer and get public key
|
||||
let signer = client.signer().await?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
log::info!("Logged in with public key: {:?}", public_key);
|
||||
|
||||
// Update signer
|
||||
client.set_signer(signer).await;
|
||||
|
||||
// Fetch user's metadata
|
||||
let metadata = client
|
||||
|
||||
@@ -33,6 +33,7 @@ smallvec.workspace = true
|
||||
smol.workspace = true
|
||||
oneshot.workspace = true
|
||||
|
||||
webbrowser = "1.0.4"
|
||||
rustls = "0.23.23"
|
||||
futures = "0.3"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["fmt"] }
|
||||
|
||||
@@ -110,7 +110,7 @@ fn main() {
|
||||
// Handle batch metadata
|
||||
app.background_executor()
|
||||
.spawn(async move {
|
||||
const BATCH_SIZE: usize = 500;
|
||||
const BATCH_SIZE: usize = 20;
|
||||
const BATCH_TIMEOUT: Duration = Duration::from_millis(300);
|
||||
|
||||
let mut batch: HashSet<PublicKey> = HashSet::new();
|
||||
@@ -143,7 +143,7 @@ fn main() {
|
||||
// Handle notifications
|
||||
app.background_executor()
|
||||
.spawn(async move {
|
||||
let rng_keys = Keys::generate();
|
||||
let keys = Keys::generate();
|
||||
let all_id = SubscriptionId::new(ALL_MESSAGES_SUB_ID);
|
||||
let new_id = SubscriptionId::new(NEW_MESSAGE_SUB_ID);
|
||||
let mut notifications = client.notifications();
|
||||
@@ -161,12 +161,12 @@ fn main() {
|
||||
Ok(event) => event,
|
||||
Err(_) => match client.unwrap_gift_wrap(&event).await {
|
||||
Ok(unwrap) => {
|
||||
match unwrap.rumor.sign_with_keys(&rng_keys) {
|
||||
Ok(ev) => {
|
||||
set_unwrapped(event.id, &ev, &rng_keys)
|
||||
match unwrap.rumor.sign_with_keys(&keys) {
|
||||
Ok(unwrapped) => {
|
||||
set_unwrapped(event.id, &unwrapped, &keys)
|
||||
.await
|
||||
.ok();
|
||||
ev
|
||||
unwrapped
|
||||
}
|
||||
Err(_) => continue,
|
||||
}
|
||||
@@ -286,6 +286,7 @@ fn main() {
|
||||
// Open a window with default options
|
||||
cx.open_window(opts, |window, cx| {
|
||||
// Automatically sync theme with system appearance
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
window
|
||||
.observe_window_appearance(|window, cx| {
|
||||
Theme::sync_system_appearance(Some(window), cx);
|
||||
|
||||
@@ -20,7 +20,17 @@ use ui::{
|
||||
ContextModal, Disableable, Sizable, Size, StyledExt,
|
||||
};
|
||||
|
||||
const INPUT_INVALID: &str = "You must provide a valid Private Key or Bunker.";
|
||||
#[derive(Debug, Clone)]
|
||||
struct CoopAuthUrlHandler;
|
||||
|
||||
impl AuthUrlHandler for CoopAuthUrlHandler {
|
||||
fn on_auth_url(&self, auth_url: Url) -> BoxedFuture<Result<()>> {
|
||||
Box::pin(async move {
|
||||
webbrowser::open(auth_url.as_str())?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(window: &mut Window, cx: &mut App) -> Entity<Login> {
|
||||
Login::new(window, cx)
|
||||
@@ -29,19 +39,21 @@ pub fn init(window: &mut Window, cx: &mut App) -> Entity<Login> {
|
||||
pub struct Login {
|
||||
// Inputs
|
||||
key_input: Entity<TextInput>,
|
||||
error_message: Entity<Option<SharedString>>,
|
||||
error: Entity<Option<SharedString>>,
|
||||
is_logging_in: bool,
|
||||
// Nostr Connect
|
||||
qr: Option<Arc<Image>>,
|
||||
qr: Entity<Option<Arc<Image>>>,
|
||||
connect_relay: Entity<TextInput>,
|
||||
connect_client: Entity<Option<NostrConnectURI>>,
|
||||
// Keep track of all signers created by nostr connect
|
||||
signers: SmallVec<[NostrConnect; 3]>,
|
||||
// Panel
|
||||
name: SharedString,
|
||||
closable: bool,
|
||||
zoomable: bool,
|
||||
focus_handle: FocusHandle,
|
||||
#[allow(unused)]
|
||||
subscriptions: SmallVec<[Subscription; 3]>,
|
||||
subscriptions: SmallVec<[Subscription; 4]>,
|
||||
}
|
||||
|
||||
impl Login {
|
||||
@@ -50,9 +62,12 @@ impl Login {
|
||||
}
|
||||
|
||||
fn view(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let connect_client: Entity<Option<NostrConnectURI>> = cx.new(|_| None);
|
||||
let error = cx.new(|_| None);
|
||||
let qr = cx.new(|_| None);
|
||||
|
||||
let signers = smallvec![];
|
||||
let mut subscriptions = smallvec![];
|
||||
let error_message = cx.new(|_| None);
|
||||
let connect_client = cx.new(|_: &mut Context<'_, Option<NostrConnectURI>>| None);
|
||||
|
||||
let key_input = cx.new(|cx| {
|
||||
TextInput::new(window, cx)
|
||||
@@ -61,7 +76,7 @@ impl Login {
|
||||
});
|
||||
|
||||
let connect_relay = cx.new(|cx| {
|
||||
let mut input = TextInput::new(window, cx).text_size(Size::Small).small();
|
||||
let mut input = TextInput::new(window, cx).text_size(Size::XSmall).small();
|
||||
input.set_text("wss://relay.nsec.app", window, cx);
|
||||
input
|
||||
});
|
||||
@@ -89,17 +104,32 @@ impl Login {
|
||||
subscriptions.push(
|
||||
cx.observe_in(&connect_client, window, |this, uri, window, cx| {
|
||||
let keys = get_client_keys().to_owned();
|
||||
let account = Account::global(cx);
|
||||
|
||||
if let Some(uri) = uri.read(cx).clone() {
|
||||
if let Ok(qr) = create_qr(uri.to_string().as_str()) {
|
||||
this.qr = Some(qr);
|
||||
cx.notify();
|
||||
this.qr.update(cx, |this, cx| {
|
||||
*this = Some(qr);
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
|
||||
match NostrConnect::new(uri, keys, Duration::from_secs(300), None) {
|
||||
Ok(signer) => {
|
||||
account.update(cx, |this, cx| {
|
||||
// Shutdown all previous nostr connect clients
|
||||
for client in std::mem::take(&mut this.signers).into_iter() {
|
||||
cx.background_spawn(async move {
|
||||
client.shutdown().await;
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
// Create a new nostr connect client
|
||||
match NostrConnect::new(uri, keys, Duration::from_secs(200), None) {
|
||||
Ok(mut signer) => {
|
||||
// Handle auth url
|
||||
signer.auth_url_handler(CoopAuthUrlHandler);
|
||||
// Store this signer for further clean up
|
||||
this.signers.push(signer.clone());
|
||||
|
||||
Account::global(cx).update(cx, |this, cx| {
|
||||
this.login(signer, window, cx);
|
||||
});
|
||||
}
|
||||
@@ -111,27 +141,16 @@ impl Login {
|
||||
}),
|
||||
);
|
||||
|
||||
cx.spawn(async move |this, cx| {
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
cx.background_executor()
|
||||
.timer(Duration::from_millis(500))
|
||||
.timer(Duration::from_millis(300))
|
||||
.await;
|
||||
|
||||
cx.update(|cx| {
|
||||
cx.update(|window, cx| {
|
||||
this.update(cx, |this, cx| {
|
||||
let Ok(relay_url) =
|
||||
RelayUrl::parse(this.connect_relay.read(cx).text().to_string().as_str())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let client_pubkey = get_client_keys().public_key();
|
||||
let uri = NostrConnectURI::client(client_pubkey, vec![relay_url], "Coop");
|
||||
|
||||
this.connect_client.update(cx, |this, cx| {
|
||||
*this = Some(uri);
|
||||
cx.notify();
|
||||
});
|
||||
this.change_relay(window, cx);
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
@@ -142,8 +161,9 @@ impl Login {
|
||||
connect_relay,
|
||||
connect_client,
|
||||
subscriptions,
|
||||
error_message,
|
||||
qr: None,
|
||||
signers,
|
||||
error,
|
||||
qr,
|
||||
is_logging_in: false,
|
||||
name: "Login".into(),
|
||||
closable: true,
|
||||
@@ -172,33 +192,21 @@ impl Login {
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
self.set_error_message(e.to_string(), cx);
|
||||
self.set_logging_in(false, cx);
|
||||
self.set_error(e.to_string(), cx);
|
||||
}
|
||||
}
|
||||
} else if content.starts_with("bunker://") {
|
||||
let keys = get_client_keys().to_owned();
|
||||
|
||||
let Ok(uri) = NostrConnectURI::parse(content.as_ref()) else {
|
||||
self.set_error_message("Bunker URL is not valid".to_owned(), cx);
|
||||
self.set_logging_in(false, cx);
|
||||
self.set_error("Bunker URL is not valid".to_owned(), cx);
|
||||
return;
|
||||
};
|
||||
|
||||
match NostrConnect::new(uri, keys, Duration::from_secs(120), None) {
|
||||
Ok(signer) => {
|
||||
account.update(cx, |this, cx| {
|
||||
this.login(signer, window, cx);
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
self.set_error_message(e.to_string(), cx);
|
||||
self.set_logging_in(false, cx);
|
||||
}
|
||||
}
|
||||
self.connect_client.update(cx, |this, cx| {
|
||||
*this = Some(uri);
|
||||
cx.notify();
|
||||
});
|
||||
} else {
|
||||
window.push_notification(Notification::error(INPUT_INVALID), cx);
|
||||
self.set_logging_in(false, cx);
|
||||
self.set_error("You must provide a valid Private Key or Bunker.".into(), cx);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -219,8 +227,9 @@ impl Login {
|
||||
});
|
||||
}
|
||||
|
||||
fn set_error_message(&mut self, message: String, cx: &mut Context<Self>) {
|
||||
self.error_message.update(cx, |this, cx| {
|
||||
fn set_error(&mut self, message: String, cx: &mut Context<Self>) {
|
||||
self.set_logging_in(false, cx);
|
||||
self.error.update(cx, |this, cx| {
|
||||
*this = Some(SharedString::new(message));
|
||||
cx.notify();
|
||||
});
|
||||
@@ -320,18 +329,15 @@ impl Render for Login {
|
||||
this.login(window, cx);
|
||||
})),
|
||||
)
|
||||
.when_some(
|
||||
self.error_message.read(cx).clone(),
|
||||
|this, error| {
|
||||
this.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.text_center()
|
||||
.text_color(cx.theme().danger)
|
||||
.child(error),
|
||||
)
|
||||
},
|
||||
),
|
||||
.when_some(self.error.read(cx).clone(), |this, error| {
|
||||
this.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.text_center()
|
||||
.text_color(cx.theme().danger)
|
||||
.child(error),
|
||||
)
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -372,12 +378,12 @@ impl Render for Login {
|
||||
.child("Use Nostr Connect apps to scan the code"),
|
||||
),
|
||||
)
|
||||
.when_some(self.qr.clone(), |this, qr| {
|
||||
.when_some(self.qr.read(cx).clone(), |this, qr| {
|
||||
this.child(
|
||||
div()
|
||||
.mb_2()
|
||||
.p_2()
|
||||
.size_64()
|
||||
.size_72()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.items_center()
|
||||
@@ -391,7 +397,7 @@ impl Render for Login {
|
||||
)
|
||||
})
|
||||
.bg(cx.theme().background)
|
||||
.child(img(qr).h_56()),
|
||||
.child(img(qr).h_64()),
|
||||
)
|
||||
})
|
||||
.child(
|
||||
@@ -406,7 +412,7 @@ impl Render for Login {
|
||||
Button::new("change")
|
||||
.label("Change")
|
||||
.ghost()
|
||||
.small()
|
||||
.xsmall()
|
||||
.on_click(cx.listener(move |this, _, window, cx| {
|
||||
this.change_relay(window, cx);
|
||||
})),
|
||||
|
||||
@@ -244,6 +244,7 @@ impl Render for Profile {
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
.px_3()
|
||||
.child(
|
||||
div()
|
||||
.w_full()
|
||||
@@ -313,7 +314,7 @@ impl Render for Profile {
|
||||
.child(self.bio_input.clone()),
|
||||
)
|
||||
.child(
|
||||
div().mt_2().w_full().child(
|
||||
div().p_3().child(
|
||||
Button::new("submit")
|
||||
.label("Update")
|
||||
.primary()
|
||||
|
||||
Reference in New Issue
Block a user