From 83d37b86ba79ebf48484d332f865834492c33e49 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 21 Dec 2021 10:45:44 +0100 Subject: Gossip only every 5 secs Signed-off-by: Matthias Beyer --- gui/src/app/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/src/app/mod.rs b/gui/src/app/mod.rs index 77f6c9d..68c56dc 100644 --- a/gui/src/app/mod.rs +++ b/gui/src/app/mod.rs @@ -337,7 +337,7 @@ impl Application for Distrox { }; let gossip_sending_sub = { - iced::time::every(std::time::Duration::from_millis(100)) + iced::time::every(std::time::Duration::from_secs(5)) .map(|_| Message::PublishGossipAboutMe) }; -- cgit v1.2.3 From a52e91767eb2cbbac96e3e6d9f14d5034ef14912 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 21 Dec 2021 11:17:34 +0100 Subject: Wrap Profile in RwLock We need mutable access to the profile if we want to write to it, so this commit changes the Profile to wrapped in a tokio::sync::RwLock Signed-off-by: Matthias Beyer --- gui/src/app/message.rs | 3 ++- gui/src/app/mod.rs | 36 +++++++++++++++++++++--------------- gui/src/gossip.rs | 5 +++-- lib/src/gossip/handler.rs | 8 ++++---- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/gui/src/app/message.rs b/gui/src/app/message.rs index dcb7477..68c95d5 100644 --- a/gui/src/app/message.rs +++ b/gui/src/app/message.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use cid::Cid; +use tokio::sync::RwLock; use distrox_lib::gossip::GossipMessage; use distrox_lib::profile::Profile; @@ -10,7 +11,7 @@ use crate::gossip::GossipRecipe; #[derive(Clone, Debug)] pub enum Message { - Loaded(Arc), + Loaded(Arc>), FailedToLoad(String), ToggleLog, diff --git a/gui/src/app/mod.rs b/gui/src/app/mod.rs index 68c56dc..1580353 100644 --- a/gui/src/app/mod.rs +++ b/gui/src/app/mod.rs @@ -1,5 +1,4 @@ use std::sync::Arc; -use std::sync::RwLock as StdRwLock; use anyhow::Result; use iced::Application; @@ -12,6 +11,7 @@ use iced::TextInput; use iced::scrollable; use iced::text_input; use distrox_lib::profile::Profile; +use tokio::sync::RwLock; use crate::timeline::Timeline; use crate::timeline::PostLoadingRecipe; @@ -24,11 +24,11 @@ use crate::gossip::GossipRecipe; #[derive(Debug)] enum Distrox { Loading { - gossip_subscription_recv: StdRwLock>, + gossip_subscription_recv: RwLock>, }, Loaded { - profile: Arc, - gossip_subscription_recv: StdRwLock>, + profile: Arc>, + gossip_subscription_recv: RwLock>, scroll: scrollable::State, input: text_input::State, @@ -50,16 +50,19 @@ impl Application for Distrox { let (gossip_subscription_sender, gossip_subscription_recv) = tokio::sync::oneshot::channel(); ( Distrox::Loading { - gossip_subscription_recv: StdRwLock::new(gossip_subscription_recv), + gossip_subscription_recv: RwLock::new(gossip_subscription_recv), }, iced::Command::perform(async move { let profile = match Profile::load(&name).await { Err(e) => return Message::FailedToLoad(e.to_string()), - Ok(instance) => Arc::new(instance), + Ok(instance) => Arc::new(RwLock::new(instance)), }; - if let Err(e) = profile.client() + if let Err(e) = profile + .read() + .await + .client() .pubsub_subscribe("distrox".to_string()) .await .map_err(anyhow::Error::from) @@ -91,7 +94,7 @@ impl Application for Distrox { profile, // Don't even try to think what hoops I am jumping through here... - gossip_subscription_recv: std::mem::replace(gossip_subscription_recv, StdRwLock::new(tokio::sync::oneshot::channel().1)), + gossip_subscription_recv: std::mem::replace(gossip_subscription_recv, RwLock::new(tokio::sync::oneshot::channel().1)), scroll: scrollable::State::default(), input: text_input::State::default(), input_value: String::default(), @@ -181,7 +184,7 @@ impl Application for Distrox { Message::PublishGossipAboutMe => { let profile = profile.clone(); iced::Command::perform(async move { - if let Err(e) = profile.gossip_own_state("distrox".to_string()).await { + if let Err(e) = profile.read().await.gossip_own_state("distrox".to_string()).await { Message::GossippingFailed(e.to_string()) } else { Message::OwnStateGossipped @@ -293,9 +296,12 @@ impl Application for Distrox { fn subscription(&self) -> iced::Subscription { let post_loading_subs = match self { Distrox::Loaded { profile, .. } => { - let head = profile.head(); + let profile = match profile.try_read() { + Err(_) => return iced::Subscription::none(), + Ok(p) => p, + }; - match head { + match profile.head() { None => iced::Subscription::none(), Some(head) => { iced::Subscription::from_recipe({ @@ -326,11 +332,11 @@ impl Application for Distrox { let gossip_sub = match self { Distrox::Loaded { gossip_subscription_recv, .. } => { - match gossip_subscription_recv.write().ok() { - Some(mut sub) => sub.try_recv() - .ok() // Either empty or closed, ignore both + match gossip_subscription_recv.try_write() { + Err(_) => None, + Ok(mut sub) => sub.try_recv() + .ok() .map(|sub| iced::Subscription::from_recipe(sub)), - None => None } }, _ => None, diff --git a/gui/src/gossip.rs b/gui/src/gossip.rs index ac6bab0..c88c29c 100644 --- a/gui/src/gossip.rs +++ b/gui/src/gossip.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use futures::StreamExt; +use tokio::sync::RwLock; use distrox_lib::profile::Profile; use distrox_lib::client::Client; @@ -9,12 +10,12 @@ use crate::app::Message; #[derive(Clone, Debug)] pub struct GossipRecipe { - profile: Arc, + profile: Arc>, subscription: Arc, } impl GossipRecipe { - pub fn new(profile: Arc, subscription: ipfs::SubscriptionStream) -> Self { + pub fn new(profile: Arc>, subscription: ipfs::SubscriptionStream) -> Self { Self { profile, subscription: Arc::new(subscription) } } } diff --git a/lib/src/gossip/handler.rs b/lib/src/gossip/handler.rs index 7c9ffa6..e524da8 100644 --- a/lib/src/gossip/handler.rs +++ b/lib/src/gossip/handler.rs @@ -18,14 +18,14 @@ use crate::gossip::GossipMessage; pub struct GossipHandler where Strategy: GossipHandlingStrategy + Sync + Send { - profile: Arc, + profile: Arc>, strategy: std::marker::PhantomData, } impl GossipHandler where Strat: GossipHandlingStrategy + Sync + Send { - pub fn new(profile: Arc) -> Self { + pub fn new(profile: Arc>) -> Self { Self { profile, strategy: std::marker::PhantomData, @@ -48,14 +48,14 @@ impl GossipHandler #[async_trait::async_trait] pub trait GossipHandlingStrategy: Sync + Send { - async fn handle_gossip_message(profile: Arc, source: &ipfs::PeerId, msg: &GossipMessage) -> Result<()>; + async fn handle_gossip_message(profile: Arc>, source: &ipfs::PeerId, msg: &GossipMessage) -> Result<()>; } pub struct LogStrategy; #[async_trait::async_trait] impl GossipHandlingStrategy for LogStrategy { - async fn handle_gossip_message(_profile: Arc, source: &ipfs::PeerId, msg: &GossipMessage) -> Result<()> { + async fn handle_gossip_message(_profile: Arc>, source: &ipfs::PeerId, msg: &GossipMessage) -> Result<()> { use std::convert::TryFrom; use std::ops::Deref; -- cgit v1.2.3 From bf097c9b5b57289e2ab792df9494f4579ce08d6c Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 21 Dec 2021 11:18:13 +0100 Subject: Post to profile, not to client Signed-off-by: Matthias Beyer --- gui/src/app/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/src/app/mod.rs b/gui/src/app/mod.rs index 1580353..f51581b 100644 --- a/gui/src/app/mod.rs +++ b/gui/src/app/mod.rs @@ -118,11 +118,11 @@ impl Application for Distrox { Message::CreatePost => { if !input_value.is_empty() { let input = input_value.clone(); - let client = profile.client().clone(); + let profile = profile.clone(); log::trace!("Posting..."); iced::Command::perform(async move { log::trace!("Posting: '{}'", input); - client.post_text_blob(input).await + profile.write().await.post_text(input).await }, |res| match res { Ok(cid) => Message::PostCreated(cid), -- cgit v1.2.3 From 2417f087194a871d25fd829996cb5a6613ec8745 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 21 Dec 2021 11:28:00 +0100 Subject: Make timeline deduplicating Signed-off-by: Matthias Beyer --- gui/src/timeline.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/gui/src/timeline.rs b/gui/src/timeline.rs index 0dace40..85cd0d5 100644 --- a/gui/src/timeline.rs +++ b/gui/src/timeline.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use anyhow::Result; use futures::StreamExt; @@ -11,6 +13,7 @@ use distrox_lib::types::Payload; #[derive(Debug)] pub struct Timeline { + post_ids: HashSet, posts: Vec, scrollable: ScrollableState, } @@ -18,13 +21,16 @@ pub struct Timeline { impl Timeline { pub fn new() -> Self { Self { + post_ids: HashSet::with_capacity(1000), posts: Vec::new(), scrollable: ScrollableState::new(), } } pub fn push(&mut self, payload: Payload, content: String) { - self.posts.push(Post::new(payload, content)); + if self.post_ids.insert(payload.content()) { + self.posts.push(Post::new(payload, content)); + } } pub fn view(&mut self) -> iced::Element { -- cgit v1.2.3 From e44417036291dd9755dc768d06355f985ad2e61d Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 21 Dec 2021 11:30:48 +0100 Subject: Dates and times can be ordered Signed-off-by: Matthias Beyer --- lib/src/types/datetime.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/types/datetime.rs b/lib/src/types/datetime.rs index 70098d7..9cc1e65 100644 --- a/lib/src/types/datetime.rs +++ b/lib/src/types/datetime.rs @@ -2,7 +2,7 @@ use std::convert::TryFrom; use anyhow::Error; use anyhow::Result; -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, serde::Serialize, serde::Deserialize)] #[serde(transparent)] pub struct DateTime(chrono::DateTime); -- cgit v1.2.3 From 5a86b1e44467f1d5439fe6f447c55f33c35f564f Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 21 Dec 2021 11:31:00 +0100 Subject: Change timeline to be ordered internally Signed-off-by: Matthias Beyer --- gui/src/timeline.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/gui/src/timeline.rs b/gui/src/timeline.rs index 85cd0d5..75f3c73 100644 --- a/gui/src/timeline.rs +++ b/gui/src/timeline.rs @@ -1,4 +1,5 @@ use std::collections::HashSet; +use std::collections::BTreeMap; use anyhow::Result; use futures::StreamExt; @@ -10,11 +11,12 @@ use crate::post::Post; use distrox_lib::client::Client; use distrox_lib::stream::NodeStreamBuilder; use distrox_lib::types::Payload; +use distrox_lib::types::DateTime; #[derive(Debug)] pub struct Timeline { post_ids: HashSet, - posts: Vec, + posts: BTreeMap, scrollable: ScrollableState, } @@ -22,14 +24,14 @@ impl Timeline { pub fn new() -> Self { Self { post_ids: HashSet::with_capacity(1000), - posts: Vec::new(), + posts: BTreeMap::new(), scrollable: ScrollableState::new(), } } pub fn push(&mut self, payload: Payload, content: String) { if self.post_ids.insert(payload.content()) { - self.posts.push(Post::new(payload, content)); + self.posts.insert(payload.timestamp().clone(), Post::new(payload, content)); } } @@ -45,7 +47,8 @@ impl Timeline { self.posts .iter() - .fold(scrollable, |scrollable, post| { + .rev() + .fold(scrollable, |scrollable, (_, post)| { scrollable.push(post.view()) }) .into() -- cgit v1.2.3 From 2300a0a871ad8e2de528bfcd4d53d1df812fd5d9 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 21 Dec 2021 11:38:56 +0100 Subject: Add saving of profile state Signed-off-by: Matthias Beyer --- gui/src/app/message.rs | 4 ++++ gui/src/app/mod.rs | 20 +++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/gui/src/app/message.rs b/gui/src/app/message.rs index 68c95d5..f19a2dd 100644 --- a/gui/src/app/message.rs +++ b/gui/src/app/message.rs @@ -13,6 +13,8 @@ use crate::gossip::GossipRecipe; pub enum Message { Loaded(Arc>), FailedToLoad(String), + ProfileStateSaved, + ProfileStateSavingFailed(String), ToggleLog, @@ -40,6 +42,8 @@ impl Message { match self { Message::Loaded(_) => "Loaded", Message::FailedToLoad(_) => "FailedToLoad", + Message::ProfileStateSaved => "ProfileStateSaved", + Message::ProfileStateSavingFailed(_) => "ProfileStateSavingFailed", Message::ToggleLog => "ToggleLog", diff --git a/gui/src/app/mod.rs b/gui/src/app/mod.rs index f51581b..a1428cd 100644 --- a/gui/src/app/mod.rs +++ b/gui/src/app/mod.rs @@ -136,9 +136,27 @@ impl Application for Distrox { Message::PostCreated(cid) => { *input_value = String::new(); log::info!("Post created: {}", cid); - iced::Command::none() + + let profile = profile.clone(); + iced::Command::perform(async move { + if let Err(e) = profile.read().await.save().await { + Message::ProfileStateSavingFailed(e.to_string()) + } else { + Message::ProfileStateSaved + } + }, |m: Message| -> Message { m }) } + Message::ProfileStateSaved => { + log::info!("Profile state saved"); + iced::Command::none() + }, + + Message::ProfileStateSavingFailed(e) => { + log::error!("Saving profile failed: {}", e); + iced::Command::none() + }, + Message::PostCreationFailed(err) => { log::error!("Post creation failed: {}", err); iced::Command::none() -- cgit v1.2.3