diff --git a/.DS_Store b/.DS_Store
index 13bff6e..ab05a38 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/Cargo.lock b/Cargo.lock
index 7e0a686..966b2e6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1094,6 +1094,7 @@ dependencies = [
"chrono",
"coop-ui",
"dirs 5.0.1",
+ "flume",
"gpui",
"itertools 0.13.0",
"keyring",
diff --git a/assets/.DS_Store b/assets/.DS_Store
index 7d1d964..ced25e0 100644
Binary files a/assets/.DS_Store and b/assets/.DS_Store differ
diff --git a/assets/icons/arrow-up-circle.svg b/assets/icons/arrow-up-circle.svg
new file mode 100644
index 0000000..a098b52
--- /dev/null
+++ b/assets/icons/arrow-up-circle.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/circle-x.svg b/assets/icons/circle-x.svg
new file mode 100644
index 0000000..2a53f1d
--- /dev/null
+++ b/assets/icons/circle-x.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/moon.svg b/assets/icons/moon.svg
new file mode 100644
index 0000000..c161221
--- /dev/null
+++ b/assets/icons/moon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/plus.svg b/assets/icons/plus.svg
new file mode 100644
index 0000000..8544f9b
--- /dev/null
+++ b/assets/icons/plus.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/sun.svg b/assets/icons/sun.svg
new file mode 100644
index 0000000..de6bb8b
--- /dev/null
+++ b/assets/icons/sun.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/upload.svg b/assets/icons/upload.svg
new file mode 100644
index 0000000..c436e83
--- /dev/null
+++ b/assets/icons/upload.svg
@@ -0,0 +1,3 @@
+
diff --git a/crates/app/Cargo.toml b/crates/app/Cargo.toml
index f9a2c6a..3f3db17 100644
--- a/crates/app/Cargo.toml
+++ b/crates/app/Cargo.toml
@@ -28,3 +28,4 @@ rust-embed.workspace = true
smol.workspace = true
tracing-subscriber = { version = "0.3.18", features = ["fmt"] }
+flume = "0.11.1"
diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs
index 38bb4c2..48a86cb 100644
--- a/crates/app/src/main.rs
+++ b/crates/app/src/main.rs
@@ -10,13 +10,14 @@ use std::{
sync::{Arc, OnceLock},
time::Duration,
};
-use tokio::{
- sync::{broadcast, mpsc},
- time::sleep,
-};
+use tokio::{sync::mpsc, time::sleep};
use constants::{ALL_MESSAGES_SUB_ID, APP_NAME, FAKE_SIG, METADATA_DELAY, NEW_MESSAGE_SUB_ID};
-use states::{account::AccountRegistry, chat::ChatRegistry, signal::SignalRegistry};
+use states::{
+ account::AccountRegistry,
+ chat::ChatRegistry,
+ metadata::{MetadataRegistry, Signal},
+};
use views::app::AppView;
pub mod asset;
@@ -74,23 +75,24 @@ async fn main() {
// Connect to all relays
_ = client.connect().await;
- // Channel for metadata signal
- let (signal_tx, mut signal_rx) = mpsc::channel::(1000); // TODO: adjust?
+ // Channel for EOSE
+ // When receive EOSE from relay(s) -> Load all rooms and push it into UI.
+ let (eose_tx, mut eose_rx) = mpsc::channel::(200);
- // Channel for new chat
- let (new_chat_tx, mut new_chat_rx) = mpsc::channel::(1000); // TODO: adjust?
+ // Channel for new message
+ // Push new message to chat panel or create new chat room if not exist.
+ let (message_tx, message_rx) = flume::unbounded::();
+ let message_rx_clone = message_rx.clone();
- // Channel for all chats
- // When receive EOSE from relay(s). Reload UI
- let (all_chats_tx, mut all_chats_rx) = mpsc::channel::(1);
-
- // Channel for metadata request queue
- let (queue_tx, mut queue_rx) = broadcast::channel::(100);
+ // Channel for signal
+ // Merge all metadata requests into single one.
+ // Notify to reload element if receive new metadata.
+ let (signal_tx, mut signal_rx) = mpsc::channel::(5000);
+ let signal_tx_clone = signal_tx.clone();
tokio::spawn(async move {
let sig = Signature::from_str(FAKE_SIG).unwrap();
- let all_messages_sub_id = SubscriptionId::new(ALL_MESSAGES_SUB_ID);
- let new_message_sub_id = SubscriptionId::new(NEW_MESSAGE_SUB_ID);
+ let new_message = SubscriptionId::new(NEW_MESSAGE_SUB_ID);
while let Ok(notification) = notifications.recv().await {
#[allow(clippy::collapsible_match)]
@@ -121,69 +123,42 @@ async fn main() {
);
// Save rumor to database to further query
- _ = client.database().save_event(&ev).await;
+ if let Err(e) = client.database().save_event(&ev).await {
+ println!("Save error: {}", e);
+ }
// Send event back to channel
- if subscription_id == new_message_sub_id {
- if let Err(e) = new_chat_tx.send(ev).await {
+ if subscription_id == new_message {
+ if let Err(e) = message_tx.send_async(ev).await {
println!("Error: {}", e)
}
}
}
}
} else if event.kind == Kind::Metadata {
- _ = signal_tx.send(event.pubkey).await;
+ if let Err(e) = signal_tx.send(Signal::DONE(event.pubkey)).await {
+ println!("Error: {}", e)
+ }
}
} else if let RelayMessage::EndOfStoredEvents(subscription_id) = message {
- if all_messages_sub_id == subscription_id {
- _ = all_chats_tx.send(1).await;
+ if let Err(e) = eose_tx.send(subscription_id).await {
+ println!("Error: {}", e)
}
}
}
}
});
- tokio::spawn(async move {
- let mut queue: HashSet = HashSet::new();
-
- while let Ok(public_key) = queue_rx.recv().await {
- queue.insert(public_key);
-
- // Wait for METADATA_DELAY
- sleep(Duration::from_millis(METADATA_DELAY)).await;
-
- if !queue.is_empty() {
- let authors: Vec = queue.iter().copied().collect();
- let total = authors.len();
-
- let filter = Filter::new()
- .authors(authors)
- .kind(Kind::Metadata)
- .limit(total);
-
- let opts = SubscribeAutoCloseOptions::default()
- .filter(FilterOptions::WaitDurationAfterEOSE(Duration::from_secs(2)));
-
- // Clear queue
- queue.clear();
-
- if let Err(e) = client.subscribe(vec![filter], Some(opts)).await {
- println!("Error: {}", e);
- }
- }
- }
- });
-
App::new()
.with_assets(Assets)
.with_http_client(Arc::new(reqwest_client::ReqwestClient::new()))
.run(move |cx| {
// Account state
AccountRegistry::set_global(cx);
+ // Metadata state
+ MetadataRegistry::set_global(cx, signal_tx_clone);
// Chat state
- ChatRegistry::set_global(cx);
- // Hold all metadata requests and merged it
- SignalRegistry::set_global(cx, Arc::new(queue_tx));
+ ChatRegistry::set_global(cx, message_rx);
// Initialize components
coop_ui::init(cx);
@@ -192,16 +167,55 @@ async fn main() {
cx.on_action(quit);
cx.spawn(|async_cx| async move {
- while let Some(public_key) = signal_rx.recv().await {
- _ = async_cx.update_global::(|state, _cx| {
- state.push(public_key);
- });
+ let mut queue: HashSet = HashSet::new();
+
+ while let Some(signal) = signal_rx.recv().await {
+ match signal {
+ Signal::REQ(public_key) => {
+ queue.insert(public_key);
+
+ // Wait for METADATA_DELAY
+ sleep(Duration::from_millis(METADATA_DELAY)).await;
+
+ if !queue.is_empty() {
+ let authors: Vec = queue.iter().copied().collect();
+ let total = authors.len();
+
+ let filter = Filter::new()
+ .authors(authors)
+ .kind(Kind::Metadata)
+ .limit(total);
+
+ let opts = SubscribeAutoCloseOptions::default().filter(
+ FilterOptions::WaitDurationAfterEOSE(Duration::from_secs(2)),
+ );
+
+ queue.clear();
+
+ async_cx
+ .background_executor()
+ .spawn(async move {
+ if let Err(e) =
+ client.subscribe(vec![filter], Some(opts)).await
+ {
+ println!("Error: {}", e);
+ }
+ })
+ .await;
+ }
+ }
+ Signal::DONE(public_key) => {
+ _ = async_cx.update_global::(|state, _| {
+ state.seen(public_key);
+ });
+ }
+ }
}
})
.detach();
cx.spawn(|async_cx| async move {
- while let Some(event) = new_chat_rx.recv().await {
+ while let Ok(event) = message_rx_clone.recv_async().await {
_ = async_cx.update_global::(|state, cx| {
state.push(event, cx);
});
@@ -210,10 +224,14 @@ async fn main() {
.detach();
cx.spawn(|async_cx| async move {
- while let Some(_n) = all_chats_rx.recv().await {
- _ = async_cx.update_global::(|state, cx| {
- state.load(cx);
- });
+ let all_messages = SubscriptionId::new(ALL_MESSAGES_SUB_ID);
+
+ while let Some(subscription_id) = eose_rx.recv().await {
+ if subscription_id == all_messages {
+ _ = async_cx.update_global::(|state, cx| {
+ state.load(cx);
+ });
+ }
}
})
.detach();
diff --git a/crates/app/src/states/account.rs b/crates/app/src/states/account.rs
index 0419e9c..4013430 100644
--- a/crates/app/src/states/account.rs
+++ b/crates/app/src/states/account.rs
@@ -58,6 +58,10 @@ impl AccountRegistry {
.detach();
}
+ pub fn get(&self) -> Option {
+ self.public_key
+ }
+
pub fn set_user(&mut self, public_key: Option) {
self.public_key = public_key
}
diff --git a/crates/app/src/states/chat.rs b/crates/app/src/states/chat.rs
index 8b02f35..d8f3614 100644
--- a/crates/app/src/states/chat.rs
+++ b/crates/app/src/states/chat.rs
@@ -1,75 +1,92 @@
+use flume::Receiver;
use gpui::*;
use itertools::Itertools;
use nostr_sdk::prelude::*;
-use std::cmp::Reverse;
use crate::get_client;
pub struct ChatRegistry {
- events: Model