feat(nostr): upgrade to recent nostr version and add error handling

This commit is contained in:
Ryan Mwangi 2024-12-13 13:56:55 +03:00
parent 86680d9c21
commit a73e7c1d68
4 changed files with 88 additions and 37 deletions

35
Cargo.lock generated
View File

@ -728,6 +728,7 @@ dependencies = [
"leptos_router", "leptos_router",
"nostr-sdk", "nostr-sdk",
"serde", "serde",
"tokio",
"uuid", "uuid",
"wasm-bindgen", "wasm-bindgen",
"web-sys", "web-sys",
@ -1524,10 +1525,11 @@ dependencies = [
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.73" version = "0.3.76"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb15147158e79fd8b8afd0252522769c4f48725460b37338544d8379d94fc8f9" checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
dependencies = [ dependencies = [
"once_cell",
"wasm-bindgen", "wasm-bindgen",
] ]
@ -3247,9 +3249,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.96" version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21d3b25c3ea1126a2ad5f4f9068483c2af1e64168f847abe863a526b8dbfe00b" checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"once_cell", "once_cell",
@ -3258,13 +3260,12 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.96" version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52857d4c32e496dc6537646b5b117081e71fd2ff06de792e3577a150627db283" checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"log", "log",
"once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.90", "syn 2.0.90",
@ -3273,9 +3274,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-futures" name = "wasm-bindgen-futures"
version = "0.4.46" version = "0.4.49"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "951fe82312ed48443ac78b66fa43eded9999f738f6022e67aead7b708659e49a" checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys", "js-sys",
@ -3286,9 +3287,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.96" version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "920b0ffe069571ebbfc9ddc0b36ba305ef65577c94b06262ed793716a1afd981" checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@ -3296,9 +3297,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.96" version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf59002391099644be3524e23b781fa43d2be0c5aa0719a18c0731b9d195cab6" checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3309,9 +3310,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.96" version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5047c5392700766601942795a436d7d2599af60dcc3cc1248c9120bfb0827b0" checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
[[package]] [[package]]
name = "wasm-streams" name = "wasm-streams"
@ -3328,9 +3329,9 @@ dependencies = [
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.73" version = "0.3.76"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "476364ff87d0ae6bfb661053a9104ab312542658c3d8f963b7ace80b6f9b26b9" checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",

View File

@ -15,11 +15,12 @@ leptos = { version = "0.6" }
leptos_meta = { version = "0.6" } leptos_meta = { version = "0.6" }
leptos_actix = { version = "0.6", optional = true } leptos_actix = { version = "0.6", optional = true }
leptos_router = { version = "0.6" } leptos_router = { version = "0.6" }
wasm-bindgen = "=0.2.96" wasm-bindgen = "=0.2.99"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
uuid = { version = "1.0", features = ["v4"] } uuid = { version = "1.0", features = ["v4"] }
web-sys = { version = "0.3", features = ["Event"] } web-sys = { version = "0.3", features = ["Event"] }
nostr-sdk = "0.37" nostr-sdk = "0.37"
tokio = "1"
[features] [features]
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"] csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]

View File

@ -5,30 +5,30 @@ use leptos_meta::*;
use crate::components::{item_form::ItemForm, items_list::ItemsList}; use crate::components::{item_form::ItemForm, items_list::ItemsList};
use crate::models::item::Item; use crate::models::item::Item;
use crate::nostr::NostrClient; use crate::nostr::NostrClient;
use std::sync::mpsc; use tokio::sync::mpsc;
use uuid::Uuid; use uuid::Uuid;
use leptos::spawn_local; use leptos::spawn_local;
use nostr_sdk::serde_json;
#[component] #[component]
pub fn App() -> impl IntoView { pub fn App() -> impl IntoView {
provide_meta_context(); provide_meta_context();
// Signal to store and update the list of items. // Signal to store and update the list of items.
let (items_signal, set_items) = create_signal(Vec::<Item>::new()); let (items_signal, set_items) = create_signal(Vec::<Item>::new());
let (tx, mut rx) = mpsc::channel::<String>(); let (tx, mut rx) = mpsc::channel::<String>(100);
spawn_local(async move { spawn_local(async move {
//initialize nostr client //initialize nostr client
let nostr_client = NostrClient::new("wss://relay.example.com").await.unwrap(); let nostr_client = NostrClient::new("wss://relay.example.com").await.unwrap();
nostr_client.subscribe_to_items(tx.clone()).await.unwrap(); nostr_client.subscribe_to_items(tx.clone()).await.unwrap();
// Handle incoming events //handle incoming events
while let Ok(content) = rx.recv(){ while let Some(content) = rx.recv().await {
if let Ok(item) = serde_json::from_str::<Item>(&content) { if let Ok(item) = serde_json::from_str::<Item>(&content) {
set_items.update(|items| items.push(item)); set_items.update(|items| items.push(item));
} }
} }
}); });
// Function to handle adding a new item to the list. // Function to handle adding a new item to the list.
let add_item = move |name: String, description: String, tags: Vec<(String, String)>| { let add_item = move |name: String, description: String, tags: Vec<(String, String)>| {
set_items.update(|items| { set_items.update(|items| {

View File

@ -1,19 +1,47 @@
use nostr_sdk::client::Error; use nostr_sdk::{client::Error, prelude::*, RelayPoolNotification};
use nostr_sdk::prelude::*; use tokio::sync::mpsc;
use nostr_sdk::RelayPoolNotification; 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<nostr_sdk::client::Error> for MyError {
fn from(err: nostr_sdk::client::Error) -> MyError {
MyError::NostrClientError(err)
}
}
impl From<nostr_sdk::event::unsigned::Error> for MyError {
fn from(err: nostr_sdk::event::unsigned::Error) -> MyError {
MyError::NostrUnsignedError(err)
}
}
pub struct NostrClient { pub struct NostrClient {
client: Client, client: Client,
keys: Keys,
} }
impl NostrClient { impl NostrClient {
pub async fn new(relay_url: &str) -> Result<Self, Error> { pub async fn new(relay_url: &str) -> Result<Self, Error> {
let keys = Keys::new(SecretKey::generate()); let keys = Keys::new(SecretKey::generate());
let client = Client::new(keys); let client = Client::new(keys.clone());
client.add_relay(relay_url).await?; client.add_relay(relay_url).await?;
client.connect().await; client.connect().await;
Ok(Self { client }) Ok(Self { client, keys })
} }
pub async fn publish_item( pub async fn publish_item(
@ -21,23 +49,44 @@ impl NostrClient {
name: String, name: String,
description: String, description: String,
tags: Vec<(String, String)> tags: Vec<(String, String)>
) -> Result<(), Error> { ) -> Result<(), MyError> {
let content = format!("{{\"name\":\"{}\",\"description\":\"{}\",\"tags\":[{}]}}", name, description, tags.iter().map(|(k, v)| format!("(\"{}\",\"{}\")", k, v)).collect::<Vec<_>>().join(",")); let content = format!(
let event = EventBuilder::new(Kind::TextNote, content).build()?; "{{\"name\":\"{}\",\"description\":\"{}\",\"tags\":[{}]}}",
self.client.send_event_builder(event).await?; name,
description,
tags.iter()
.map(|(k, v)| format!("(\"{}\",\"{}\")", k, v))
.collect::<Vec<_>>()
.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(()) Ok(())
} }
pub async fn subscribe_to_items( pub async fn subscribe_to_items(
&self, &self,
tx: std::sync::mpsc::Sender<String> tx: mpsc::Sender<String>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let mut notifications = self.client.notifications(); 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 { 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(); let content = event.content.clone();
tx.send(content).await.unwrap(); if tx.send(content).await.is_err() {
eprintln!("Failed to send message");
}
} }
} }
}); });