notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit 5619ae60ad42c974d87ed0b6afe2557078fbe98d
parent e6571d8847bb06b0de97c4b61657276a8ccee5d9
Author: William Casarin <jb55@jb55.com>
Date:   Sun, 11 Dec 2022 17:09:44 -0800

relay connected!

Diffstat:
MCargo.lock | 53+++++++++++++++++++++++++++++++++--------------------
MCargo.toml | 7+++++--
Menostr/Cargo.lock | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Menostr/Cargo.toml | 3++-
Aenostr/src/client/message.rs | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Aenostr/src/client/mod.rs | 1+
Aenostr/src/filter.rs | 23+++++++++++++++++++++++
Menostr/src/lib.rs | 2++
Menostr/src/relay/mod.rs | 4++--
Menostr/src/relay/pool.rs | 31+++++++++++++++++++++----------
Msrc/app.rs | 48+++++++++++++++++++++++++++++++-----------------
Msrc/bin/main.rs | 3++-
12 files changed, 266 insertions(+), 53 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -646,12 +646,12 @@ dependencies = [ "egui_extras", "ehttp", "enostr", - "ewebsock 0.2.0 (git+https://github.com/jb55/ewebsock.git?rev=93420aa96a990e7513647db775751a2dc9d4a1ba)", "image", "log", "poll-promise", "serde", "serde_derive", + "tokio", "tracing", "tracing-subscriber", "tracing-wasm", @@ -918,10 +918,11 @@ dependencies = [ name = "enostr" version = "0.1.0" dependencies = [ - "ewebsock 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ewebsock", "serde", "serde_derive", "serde_json", + "tracing", ] [[package]] @@ -1001,24 +1002,6 @@ dependencies = [ ] [[package]] -name = "ewebsock" -version = "0.2.0" -source = "git+https://github.com/jb55/ewebsock.git?rev=93420aa96a990e7513647db775751a2dc9d4a1ba#93420aa96a990e7513647db775751a2dc9d4a1ba" -dependencies = [ - "async-stream", - "futures", - "futures-util", - "js-sys", - "tokio", - "tokio-tungstenite", - "tracing", - "tungstenite", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] name = "expat-sys" version = "2.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2797,12 +2780,36 @@ dependencies = [ "libc", "memchr", "mio", + "num_cpus", "pin-project-lite", "socket2", + "tokio-macros", "winapi", ] [[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] name = "tokio-tungstenite" version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2810,8 +2817,12 @@ checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" dependencies = [ "futures-util", "log", + "rustls", "tokio", + "tokio-rustls", "tungstenite", + "webpki", + "webpki-roots", ] [[package]] @@ -2917,10 +2928,12 @@ dependencies = [ "httparse", "log", "rand", + "rustls", "sha-1", "thiserror", "url", "utf-8", + "webpki", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml @@ -23,8 +23,6 @@ poll-promise = "0.2.0" serde_derive = "1" serde = { version = "1", features = ["derive"] } # You only need this if you want app persistence tracing = "0.1.37" -tracing-subscriber = "0.3" -ewebsock = { git = "https://github.com/jb55/ewebsock.git", rev = "93420aa96a990e7513647db775751a2dc9d4a1ba" } #wasm-bindgen = "0.2.83" #wasm-bindgen-futures = "0.4" enostr = { path = "enostr" } @@ -35,6 +33,11 @@ enostr = { path = "enostr" } console_error_panic_hook = "0.1.6" tracing-wasm = "0.2" +# native: +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +tokio = { version = "1.16", features = ["macros", "rt-multi-thread"] } +tracing-subscriber = "0.3" + [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.11.1" #android-activity = "0.4.0" diff --git a/enostr/Cargo.lock b/enostr/Cargo.lock @@ -63,6 +63,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" [[package]] +name = "cc" +version = "1.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" + +[[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -105,6 +111,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", + "tracing", ] [[package]] @@ -405,12 +412,49 @@ dependencies = [ ] [[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustls" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] name = "ryu" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] name = "serde" version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -472,6 +516,12 @@ dependencies = [ ] [[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] name = "syn" version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -534,6 +584,17 @@ dependencies = [ ] [[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] name = "tokio-tungstenite" version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -541,8 +602,12 @@ checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" dependencies = [ "futures-util", "log", + "rustls", "tokio", + "tokio-rustls", "tungstenite", + "webpki", + "webpki-roots", ] [[package]] @@ -590,10 +655,12 @@ dependencies = [ "httparse", "log", "rand", + "rustls", "sha-1", "thiserror", "url", "utf-8", + "webpki", ] [[package]] @@ -624,6 +691,12 @@ dependencies = [ ] [[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] name = "url" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -729,6 +802,25 @@ dependencies = [ ] [[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + +[[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/enostr/Cargo.toml b/enostr/Cargo.toml @@ -6,7 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ewebsock = "0.2.0" +ewebsock = { version = "0.2.0", features = ["tls"] } serde_derive = "1" serde = { version = "1", features = ["derive"] } # You only need this if you want app persistence serde_json = "1.0.89" +tracing = "0.1.37" diff --git a/enostr/src/client/message.rs b/enostr/src/client/message.rs @@ -0,0 +1,52 @@ +use crate::{Event, Filter} + +/// Messages sent by clients, received by relays +#[derive(Debug, Eq, PartialEq)] +pub enum ClientMessage { + Event { + event: Event, + }, + Req { + sub_id: String, + filters: Vec<Filter>, + }, + Close { + sub_id: String, + }, +} + +impl ClientMessage { + pub fn event(ev: Event) -> Self { + ClientMessage::Event {ev} + } + + pub fn req(sub_id: String, filters: Vec<Filter>) -> Self { + ClientMessage::Req { sub_id, filters } + } + + pub fn close(sub_id: String) -> Self { + ClientMessage::Close { sub_id } + } + + pub fn to_json(&self) -> String { + match self { + Self::Event { event } => json!(["EVENT", event]).to_string(), + Self::Req { + subscription_id, + filters, + } => { + let mut json = json!(["REQ", subscription_id]); + let mut filters = json!(filters); + + if let Some(json) = json.as_array_mut() { + if let Some(filters) = filters.as_array_mut() { + json.append(filters); + } + } + + json.to_string() + } + Self::Close { subscription_id } => json!(["CLOSE", subscription_id]).to_string(), + } + } +} diff --git a/enostr/src/client/mod.rs b/enostr/src/client/mod.rs @@ -0,0 +1 @@ +mod message; diff --git a/enostr/src/filter.rs b/enostr/src/filter.rs @@ -0,0 +1,23 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] +pub struct Filter { + #[serde(skip_serializing_if = "Option::is_none")] + ids: Option<Vec<String>>, + #[serde(skip_serializing_if = "Option::is_none")] + authors: Option<Vec<String>>, + #[serde(skip_serializing_if = "Option::is_none")] + kinds: Option<Vec<u64>>, + #[serde(rename = "#e")] + #[serde(skip_serializing_if = "Option::is_none")] + events: Option<Vec<String>>, + #[serde(rename = "#p")] + #[serde(skip_serializing_if = "Option::is_none")] + pubkeys: Option<Vec<String>>, + #[serde(skip_serializing_if = "Option::is_none")] + since: Option<u64>, // unix timestamp seconds + #[serde(skip_serializing_if = "Option::is_none")] + until: Option<u64>, // unix timestamp seconds + #[serde(skip_serializing_if = "Option::is_none")] + limit: Option<u16>, +} diff --git a/enostr/src/lib.rs b/enostr/src/lib.rs @@ -1,9 +1,11 @@ mod error; mod event; +mod filter; mod relay; pub use error::Error; pub use event::Event; +pub use filter::Filter; pub use relay::pool::RelayPool; pub use relay::Relay; diff --git a/enostr/src/relay/mod.rs b/enostr/src/relay/mod.rs @@ -46,9 +46,9 @@ impl PartialEq for Relay { impl Eq for Relay {} impl Relay { - pub fn new(url: String) -> Result<Self> { + pub fn new(url: String, wakeup: impl Fn() + Send + Sync + 'static) -> Result<Self> { let status = RelayStatus::Connecting; - let (sender, receiver) = ewebsock::connect(&url)?; + let (sender, receiver) = ewebsock::connect_with_wakeup(&url, wakeup)?; Ok(Self { url, diff --git a/enostr/src/relay/pool.rs b/enostr/src/relay/pool.rs @@ -1,6 +1,7 @@ use crate::relay::message::RelayEvent; use crate::relay::Relay; use crate::Result; +use tracing::error; #[derive(Debug)] pub struct PoolMessage<'a> { @@ -20,8 +21,8 @@ impl Default for RelayPool { impl RelayPool { // Constructs a new, empty RelayPool. - pub fn new(relays: Vec<Relay>) -> RelayPool { - RelayPool { relays: relays } + pub fn new() -> RelayPool { + RelayPool { relays: vec![] } } pub fn has(&self, url: &str) -> bool { @@ -34,8 +35,12 @@ impl RelayPool { } // Adds a websocket url to the RelayPool. - pub fn add_url(&mut self, url: String) -> Result<()> { - let relay = Relay::new(url)?; + pub fn add_url( + &mut self, + url: String, + wakeup: impl Fn() + Send + Sync + 'static, + ) -> Result<()> { + let relay = Relay::new(url, wakeup)?; self.relays.push(relay); @@ -45,12 +50,18 @@ impl RelayPool { pub fn try_recv(&self) -> Option<PoolMessage<'_>> { for relay in &self.relays { if let Some(msg) = relay.receiver.try_recv() { - if let Ok(event) = msg.try_into() { - let pmsg = PoolMessage { - event, - relay: &relay.url, - }; - return Some(pmsg); + match msg.try_into() { + Ok(event) => { + return Some(PoolMessage { + event, + relay: &relay.url, + }); + } + + Err(e) => { + error!("{:?}", e); + continue; + } } } } diff --git a/src/app.rs b/src/app.rs @@ -6,10 +6,10 @@ use egui_extras::RetainedImage; use poll_promise::Promise; //use std::borrow::{Borrow, Cow}; use egui::Context; -use log::error; +//use log::error; use std::collections::HashMap; use std::hash::Hash; -use tracing::debug; +use tracing::{debug, error, info}; use enostr::{Event, RelayPool}; @@ -21,28 +21,26 @@ enum UrlKey<'a> { type ImageCache<'a> = HashMap<UrlKey<'a>, Promise<ehttp::Result<RetainedImage>>>; +#[derive(Eq, PartialEq, Clone)] +pub enum DamusState { + Initializing, + Initialized, +} + /// We derive Deserialize/Serialize so we can persist app state on shutdown. -#[derive(serde::Deserialize, serde::Serialize)] -#[serde(default)] // if we add new fields, give them default values when deserializing old state pub struct Damus<'a> { // Example stuff: label: String, - + state: DamusState, composing: bool, - n_panels: u32, - #[serde(skip)] pool: RelayPool, - #[serde(skip)] events: Vec<Event>, - #[serde(skip)] img_cache: ImageCache<'a>, - // this how you opt-out of serialization of a member - #[serde(skip)] value: f32, } @@ -51,6 +49,7 @@ impl Default for Damus<'_> { Self { // Example stuff: label: "Hello World!".to_owned(), + state: DamusState::Initializing, composing: false, pool: RelayPool::default(), events: vec![], @@ -66,8 +65,24 @@ pub fn is_mobile(ctx: &egui::Context) -> bool { screen_size.x < 550.0 } -fn damus_update(damus: &mut Damus, ctx: &Context) { - render_damus(damus, ctx); +fn relay_setup(pool: &mut RelayPool, ctx: &egui::Context) { + let ctx = ctx.clone(); + let wakeup = move || ctx.request_repaint(); + if let Err(e) = pool.add_url("wss://relay.damus.io".to_string(), wakeup) { + error!("{:?}", e) + } +} + +fn update_damus(damus: &mut Damus, ctx: &egui::Context) { + if damus.state == DamusState::Initializing { + damus.pool = RelayPool::new(); + relay_setup(&mut damus.pool, ctx); + damus.state = DamusState::Initialized; + } + + if let Some(ev) = damus.pool.try_recv() { + info!("recv {:?}", ev) + } } fn render_damus(damus: &mut Damus, ctx: &Context) { @@ -94,9 +109,7 @@ impl Damus<'_> { //return eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default(); //} - let mut d: Self = Default::default(); - d.add_test_events(); - d + Default::default() } } @@ -348,7 +361,8 @@ impl eframe::App for Damus<'_> { /// Called each time the UI needs repainting, which may be many times per second. /// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`. fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - damus_update(self, ctx); + update_damus(self, ctx); + render_damus(self, ctx); } } diff --git a/src/bin/main.rs b/src/bin/main.rs @@ -9,7 +9,8 @@ use eframe; // Desktop #[cfg(not(target_arch = "wasm32"))] -fn main() { +#[tokio::main] +async fn main() { // Log to stdout (if you run with `RUST_LOG=debug`). tracing_subscriber::fmt::init();