From a73e7c1d68151f9a1eebdf5264759f5ac1190497 Mon Sep 17 00:00:00 2001 From: Ryan Mwangi Date: Fri, 13 Dec 2024 13:56:55 +0300 Subject: [PATCH] feat(nostr): upgrade to recent nostr version and add error handling --- Cargo.lock | 35 ++++++++++++------------ Cargo.toml | 3 +- src/app.rs | 10 +++---- src/nostr.rs | 77 ++++++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 88 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 39cbb5f..db75d31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -728,6 +728,7 @@ dependencies = [ "leptos_router", "nostr-sdk", "serde", + "tokio", "uuid", "wasm-bindgen", "web-sys", @@ -1524,10 +1525,11 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.73" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb15147158e79fd8b8afd0252522769c4f48725460b37338544d8379d94fc8f9" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -3247,9 +3249,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.96" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21d3b25c3ea1126a2ad5f4f9068483c2af1e64168f847abe863a526b8dbfe00b" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -3258,13 +3260,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.96" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52857d4c32e496dc6537646b5b117081e71fd2ff06de792e3577a150627db283" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn 2.0.90", @@ -3273,9 +3274,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.46" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "951fe82312ed48443ac78b66fa43eded9999f738f6022e67aead7b708659e49a" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", @@ -3286,9 +3287,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.96" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "920b0ffe069571ebbfc9ddc0b36ba305ef65577c94b06262ed793716a1afd981" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3296,9 +3297,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.96" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf59002391099644be3524e23b781fa43d2be0c5aa0719a18c0731b9d195cab6" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", @@ -3309,9 +3310,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.96" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5047c5392700766601942795a436d7d2599af60dcc3cc1248c9120bfb0827b0" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "wasm-streams" @@ -3328,9 +3329,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.73" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "476364ff87d0ae6bfb661053a9104ab312542658c3d8f963b7ace80b6f9b26b9" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index 4248caf..58aba4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,11 +15,12 @@ leptos = { version = "0.6" } leptos_meta = { version = "0.6" } leptos_actix = { version = "0.6", optional = true } leptos_router = { version = "0.6" } -wasm-bindgen = "=0.2.96" +wasm-bindgen = "=0.2.99" serde = { version = "1.0", features = ["derive"] } uuid = { version = "1.0", features = ["v4"] } web-sys = { version = "0.3", features = ["Event"] } nostr-sdk = "0.37" +tokio = "1" [features] csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"] diff --git a/src/app.rs b/src/app.rs index 6abd8f9..33bfe43 100644 --- a/src/app.rs +++ b/src/app.rs @@ -5,30 +5,30 @@ use leptos_meta::*; use crate::components::{item_form::ItemForm, items_list::ItemsList}; use crate::models::item::Item; use crate::nostr::NostrClient; -use std::sync::mpsc; +use tokio::sync::mpsc; use uuid::Uuid; use leptos::spawn_local; +use nostr_sdk::serde_json; #[component] pub fn App() -> impl IntoView { provide_meta_context(); // Signal to store and update the list of items. let (items_signal, set_items) = create_signal(Vec::::new()); - let (tx, mut rx) = mpsc::channel::(); + let (tx, mut rx) = mpsc::channel::(100); spawn_local(async move { //initialize nostr client let nostr_client = NostrClient::new("wss://relay.example.com").await.unwrap(); nostr_client.subscribe_to_items(tx.clone()).await.unwrap(); - // Handle incoming events - while let Ok(content) = rx.recv(){ + //handle incoming events + while let Some(content) = rx.recv().await { if let Ok(item) = serde_json::from_str::(&content) { set_items.update(|items| items.push(item)); } } }); - // Function to handle adding a new item to the list. let add_item = move |name: String, description: String, tags: Vec<(String, String)>| { set_items.update(|items| { diff --git a/src/nostr.rs b/src/nostr.rs index 0b9bef4..1f6ae26 100644 --- a/src/nostr.rs +++ b/src/nostr.rs @@ -1,19 +1,47 @@ -use nostr_sdk::client::Error; -use nostr_sdk::prelude::*; -use nostr_sdk::RelayPoolNotification; +use nostr_sdk::{client::Error, prelude::*, RelayPoolNotification}; +use tokio::sync::mpsc; +use std::fmt; + +#[derive(Debug)] +pub enum MyError { + NostrClientError(nostr_sdk::client::Error), + NostrUnsignedError(nostr_sdk::event::unsigned::Error), +} + +impl fmt::Display for MyError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + MyError::NostrClientError(e) => write!(f, "Nostr Client Error: {}", e), + MyError::NostrUnsignedError(e) => write!(f, "Nostr Unsigned Error: {}", e), + } + } +} + +impl From for MyError { + fn from(err: nostr_sdk::client::Error) -> MyError { + MyError::NostrClientError(err) + } +} + +impl From for MyError { + fn from(err: nostr_sdk::event::unsigned::Error) -> MyError { + MyError::NostrUnsignedError(err) + } +} pub struct NostrClient { client: Client, + keys: Keys, } impl NostrClient { pub async fn new(relay_url: &str) -> Result { let keys = Keys::new(SecretKey::generate()); - let client = Client::new(keys); + let client = Client::new(keys.clone()); client.add_relay(relay_url).await?; client.connect().await; - Ok(Self { client }) + Ok(Self { client, keys }) } pub async fn publish_item( @@ -21,23 +49,44 @@ impl NostrClient { name: String, description: String, tags: Vec<(String, String)> - ) -> Result<(), Error> { - let content = format!("{{\"name\":\"{}\",\"description\":\"{}\",\"tags\":[{}]}}", name, description, tags.iter().map(|(k, v)| format!("(\"{}\",\"{}\")", k, v)).collect::>().join(",")); - let event = EventBuilder::new(Kind::TextNote, content).build()?; - self.client.send_event_builder(event).await?; + ) -> Result<(), MyError> { + let content = format!( + "{{\"name\":\"{}\",\"description\":\"{}\",\"tags\":[{}]}}", + name, + description, + tags.iter() + .map(|(k, v)| format!("(\"{}\",\"{}\")", k, v)) + .collect::>() + .join(",") + ); + + // Create the event builder + let event_builder = EventBuilder::new(Kind::TextNote, content); + // Build the unsigned event + let unsigned_event = event_builder.build(self.keys.public_key()); + // Sign the event and handle the error explicitly + let signed_event = match unsigned_event.sign(&self.keys).await { + Ok(event) => event, + Err(e) => return Err(MyError::from(e)), // Convert the error to the expected type + }; + + // Send the event + self.client.send_event(signed_event).await?; Ok(()) } - pub async fn subscribe_to_items( &self, - tx: std::sync::mpsc::Sender + tx: mpsc::Sender, ) -> Result<(), Error> { let mut notifications = self.client.notifications(); - std::thread::spawn(move || { + // Use tokio::spawn for async context + tokio::spawn(async move { while let Ok(notification) = notifications.recv().await { - if let RelayPoolNotification::Event { relay_url, subscription_id, event } = notification { + if let RelayPoolNotification::Event { relay_url: _, subscription_id: _, event } = notification { let content = event.content.clone(); - tx.send(content).await.unwrap(); + if tx.send(content).await.is_err() { + eprintln!("Failed to send message"); + } } } });