From eb0d67852112e07b742aff6c5147c4fbcdf0b5b7 Mon Sep 17 00:00:00 2001 From: Ryan Mwangi Date: Tue, 10 Dec 2024 22:28:38 +0300 Subject: [PATCH] feat(nostr): integrate nostr --- src/app.rs | 35 ++++++++++++++++++++++++++++++----- src/lib.rs | 1 + src/nostr.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 src/nostr.rs diff --git a/src/app.rs b/src/app.rs index 2e54836..2b5c203 100644 --- a/src/app.rs +++ b/src/app.rs @@ -4,23 +4,48 @@ use leptos::*; use leptos_meta::*; use crate::components::{item_form::ItemForm, items_list::ItemsList}; use crate::models::item::Item; +use crate::nostr::NostrClient; +use tokio::sync::mpsc; use uuid::Uuid; +use serde_json; +use leptos::spawn_local; #[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::(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 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| { - items.push(Item { + let item = Item { id: Uuid::new_v4().to_string(), - name, - description, - tags, - }); + name: name.clone(), + description: description.clone(), + tags: tags.clone(), + }; + items.push(item); + }); + + spawn_local(async move { + let nostr_client = NostrClient::new("wss://relay.example.com").await.unwrap(); + nostr_client.publish_item(name, description, tags).await.unwrap(); }); }; diff --git a/src/lib.rs b/src/lib.rs index 4e3b8f2..10f7ef7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ pub mod app; pub mod components; pub mod models; +pub mod nostr; #[cfg(feature = "hydrate")] diff --git a/src/nostr.rs b/src/nostr.rs new file mode 100644 index 0000000..30f7768 --- /dev/null +++ b/src/nostr.rs @@ -0,0 +1,51 @@ +use nostr_sdk::client::Error; +use nostr_sdk::prelude::*; +use tokio::sync::mpsc; +use nostr_sdk::RelayPoolNotification; + +pub struct NostrClient { + client: Client, +} + +impl NostrClient { + pub async fn new(relay_url: &str) -> Result { + let keys = Keys::generate_from_os_random(); + let client = Client::new(&keys); + client.add_relay(relay_url, None).await?; + client.connect().await; + + Ok(Self { client }) + } + + pub async fn publish_item( + &self, + name: String, + description: String, + tags: Vec<(String, String)> + ) -> Result<(), Error> { + let content = serde_json::json!({ + "name": name, + "description": description, + "tags": tags + }); + self.client.publish_text_note(content.to_string(), &[]).await?; + Ok(()) + } + + pub async fn subscribe_to_items( + &self, + tx: mpsc::Sender + ) -> Result<(), Error> { + let mut notifications = self.client.notifications(); + tokio::spawn(async move { + while let Ok(notification) = notifications.recv().await { + if let RelayPoolNotification::Event(_url, event) = notification { + let content = event.content.clone(); + tx.send(content).await.unwrap(); + } + } + }); + + Ok(()) + } +}