.
This commit is contained in:
@@ -16,7 +16,7 @@ use gpui::{
|
|||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
use smol::lock::RwLock;
|
use smol::lock::RwLock;
|
||||||
use state::{CoopSigner, DEVICE_GIFTWRAP, NostrRegistry, StateEvent, TIMEOUT, USER_GIFTWRAP};
|
use state::{CoopSigner, DEVICE_GIFTWRAP, NostrRegistry, StateEvent, USER_GIFTWRAP};
|
||||||
|
|
||||||
mod message;
|
mod message;
|
||||||
mod room;
|
mod room;
|
||||||
@@ -41,6 +41,8 @@ pub enum ChatEvent {
|
|||||||
CloseRoom(u64),
|
CloseRoom(u64),
|
||||||
/// An event to notify UI about a new chat request
|
/// An event to notify UI about a new chat request
|
||||||
Ping,
|
Ping,
|
||||||
|
/// No Inbox Relays found, the app is not ready to subscribe messages
|
||||||
|
InboxRelayNotFound,
|
||||||
/// An error occurred
|
/// An error occurred
|
||||||
Error(SharedString),
|
Error(SharedString),
|
||||||
}
|
}
|
||||||
@@ -85,9 +87,6 @@ impl Signal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Dekey = bool;
|
|
||||||
type GiftWrapId = EventId;
|
|
||||||
|
|
||||||
/// Chat Registry
|
/// Chat Registry
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ChatRegistry {
|
pub struct ChatRegistry {
|
||||||
@@ -101,10 +100,13 @@ pub struct ChatRegistry {
|
|||||||
seens: Arc<RwLock<HashMap<EventId, HashSet<RelayUrl>>>>,
|
seens: Arc<RwLock<HashMap<EventId, HashSet<RelayUrl>>>>,
|
||||||
|
|
||||||
/// Mapping of unwrapped event ids to their gift wrap event ids
|
/// Mapping of unwrapped event ids to their gift wrap event ids
|
||||||
event_map: Arc<RwLock<HashMap<EventId, (GiftWrapId, Dekey)>>>,
|
event_map: Arc<RwLock<HashMap<EventId, EventId>>>,
|
||||||
|
|
||||||
/// Tracking the status of unwrapping gift wrap events.
|
/// Tracking the status of unwrapping gift wrap events.
|
||||||
tracking_flag: Arc<AtomicBool>,
|
tracking: Arc<AtomicBool>,
|
||||||
|
|
||||||
|
/// Whether the messaging relays have been found.
|
||||||
|
msg_relays_existed: Arc<AtomicBool>,
|
||||||
|
|
||||||
/// Channel for sending signals to the UI.
|
/// Channel for sending signals to the UI.
|
||||||
signal_tx: flume::Sender<Signal>,
|
signal_tx: flume::Sender<Signal>,
|
||||||
@@ -162,7 +164,8 @@ impl ChatRegistry {
|
|||||||
trashes: cx.new(|_| BTreeSet::default()),
|
trashes: cx.new(|_| BTreeSet::default()),
|
||||||
seens: Arc::new(RwLock::new(HashMap::default())),
|
seens: Arc::new(RwLock::new(HashMap::default())),
|
||||||
event_map: Arc::new(RwLock::new(HashMap::default())),
|
event_map: Arc::new(RwLock::new(HashMap::default())),
|
||||||
tracking_flag: Arc::new(AtomicBool::new(false)),
|
tracking: Arc::new(AtomicBool::new(false)),
|
||||||
|
msg_relays_existed: Arc::new(AtomicBool::new(false)),
|
||||||
signal_rx: rx,
|
signal_rx: rx,
|
||||||
signal_tx: tx,
|
signal_tx: tx,
|
||||||
tasks: smallvec![],
|
tasks: smallvec![],
|
||||||
@@ -175,7 +178,10 @@ impl ChatRegistry {
|
|||||||
let nostr = NostrRegistry::global(cx);
|
let nostr = NostrRegistry::global(cx);
|
||||||
let client = nostr.read(cx).client();
|
let client = nostr.read(cx).client();
|
||||||
let signer = nostr.read(cx).signer();
|
let signer = nostr.read(cx).signer();
|
||||||
let status = self.tracking_flag.clone();
|
|
||||||
|
let tracking = self.tracking.clone();
|
||||||
|
let msg_relays_existed = self.msg_relays_existed.clone();
|
||||||
|
|
||||||
let seens = self.seens.clone();
|
let seens = self.seens.clone();
|
||||||
let event_map = self.event_map.clone();
|
let event_map = self.event_map.clone();
|
||||||
let trashes = self.trashes.downgrade();
|
let trashes = self.trashes.downgrade();
|
||||||
@@ -199,10 +205,7 @@ impl ChatRegistry {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match *message {
|
match *message {
|
||||||
RelayMessage::Event {
|
RelayMessage::Event { event, .. } => {
|
||||||
event,
|
|
||||||
subscription_id,
|
|
||||||
} => {
|
|
||||||
// Keep track of which relays have seen this event
|
// Keep track of which relays have seen this event
|
||||||
{
|
{
|
||||||
let mut seens = seens.write().await;
|
let mut seens = seens.write().await;
|
||||||
@@ -219,6 +222,9 @@ impl ChatRegistry {
|
|||||||
let current_user = signer.get_public_key().await?;
|
let current_user = signer.get_public_key().await?;
|
||||||
|
|
||||||
if event.pubkey == current_user {
|
if event.pubkey == current_user {
|
||||||
|
// Mark that the msg relays have been found
|
||||||
|
msg_relays_existed.store(true, Ordering::Release);
|
||||||
|
// Emit the inbox ready signal
|
||||||
tx.send_async(Signal::inbox_ready(&event)).await?;
|
tx.send_async(Signal::inbox_ready(&event)).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,8 +240,7 @@ impl ChatRegistry {
|
|||||||
// Map the rumor id to the gift wrap event id for later lookup
|
// Map the rumor id to the gift wrap event id for later lookup
|
||||||
{
|
{
|
||||||
let mut event_map = event_map.write().await;
|
let mut event_map = event_map.write().await;
|
||||||
let dekey = subscription_id.as_ref() == &sub_id1;
|
event_map.insert(rumor.id.unwrap(), event.id);
|
||||||
event_map.insert(rumor.id.unwrap(), (event.id, dekey));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the rumor has a recipient
|
// Check if the rumor has a recipient
|
||||||
@@ -250,7 +255,7 @@ impl ChatRegistry {
|
|||||||
tx.send_async(signal).await?;
|
tx.send_async(signal).await?;
|
||||||
} else {
|
} else {
|
||||||
// Mark the chat still processing new messages
|
// Mark the chat still processing new messages
|
||||||
status.store(true, Ordering::Release);
|
tracking.store(true, Ordering::Release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -287,7 +292,7 @@ impl ChatRegistry {
|
|||||||
}
|
}
|
||||||
Signal::InboxRelayNotFound => {
|
Signal::InboxRelayNotFound => {
|
||||||
this.update(cx, |_this, cx| {
|
this.update(cx, |_this, cx| {
|
||||||
cx.emit(ChatEvent::Error("Messaging Relays not found".into()));
|
cx.emit(ChatEvent::InboxRelayNotFound);
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
Signal::Eose => {
|
Signal::Eose => {
|
||||||
@@ -310,7 +315,7 @@ impl ChatRegistry {
|
|||||||
|
|
||||||
/// Tracking the status of unwrapping gift wrap events.
|
/// Tracking the status of unwrapping gift wrap events.
|
||||||
fn tracking(&mut self, cx: &mut Context<Self>) {
|
fn tracking(&mut self, cx: &mut Context<Self>) {
|
||||||
let status = self.tracking_flag.clone();
|
let status = self.tracking.clone();
|
||||||
let tx = self.signal_tx.clone();
|
let tx = self.signal_tx.clone();
|
||||||
|
|
||||||
self.tasks.push(cx.background_spawn(async move {
|
self.tasks.push(cx.background_spawn(async move {
|
||||||
@@ -328,8 +333,8 @@ impl ChatRegistry {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get contact list from relays
|
/// Get all necessary metadata from relays for current user
|
||||||
fn get_metadata(&mut self, cx: &mut Context<Self>) {
|
pub fn get_metadata(&mut self, cx: &mut Context<Self>) {
|
||||||
let nostr = NostrRegistry::global(cx);
|
let nostr = NostrRegistry::global(cx);
|
||||||
let client = nostr.read(cx).client();
|
let client = nostr.read(cx).client();
|
||||||
let signer = nostr.read(cx).signer();
|
let signer = nostr.read(cx).signer();
|
||||||
@@ -339,9 +344,7 @@ impl ChatRegistry {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.tasks.push(cx.background_spawn(async move {
|
self.tasks.push(cx.background_spawn(async move {
|
||||||
let opts = SubscribeAutoCloseOptions::default()
|
let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE);
|
||||||
.exit_policy(ReqExitPolicy::ExitOnEOSE)
|
|
||||||
.timeout(Some(Duration::from_secs(TIMEOUT)));
|
|
||||||
|
|
||||||
// Construct filter for msg relays
|
// Construct filter for msg relays
|
||||||
let msg_relays = Filter::new()
|
let msg_relays = Filter::new()
|
||||||
@@ -358,29 +361,27 @@ impl ChatRegistry {
|
|||||||
// Subscribe
|
// Subscribe
|
||||||
client
|
client
|
||||||
.subscribe(vec![msg_relays, contact_list])
|
.subscribe(vec![msg_relays, contact_list])
|
||||||
|
.with_id(SubscriptionId::new("user-meta"))
|
||||||
.close_on(opts)
|
.close_on(opts)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let client = nostr.read(cx).client();
|
|
||||||
let tx = self.signal_tx.clone();
|
let tx = self.signal_tx.clone();
|
||||||
|
let msg_relays_existed = self.msg_relays_existed.clone();
|
||||||
|
|
||||||
|
// Reset the status flag
|
||||||
|
msg_relays_existed.store(false, Ordering::Release);
|
||||||
|
|
||||||
|
// Wait for the msg relays to be found or timeout
|
||||||
self.tasks.push(cx.background_spawn(async move {
|
self.tasks.push(cx.background_spawn(async move {
|
||||||
loop {
|
// Wait for 5 seconds
|
||||||
let filter = Filter::new()
|
smol::Timer::after(Duration::from_secs(5)).await;
|
||||||
.kind(Kind::InboxRelays)
|
|
||||||
.author(public_key)
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if client.database().query(filter).await?.first().is_some() {
|
// Then check if the msg relays have been found
|
||||||
break;
|
if !msg_relays_existed.load(Ordering::Acquire) {
|
||||||
} else {
|
tx.send_async(Signal::inbox_relay_not_found()).await?;
|
||||||
tx.send_async(Signal::inbox_relay_not_found()).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
smol::Timer::after(Duration::from_secs(5)).await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -439,7 +440,7 @@ impl ChatRegistry {
|
|||||||
|
|
||||||
/// Get the loading status of the chat registry
|
/// Get the loading status of the chat registry
|
||||||
pub fn loading(&self) -> bool {
|
pub fn loading(&self) -> bool {
|
||||||
self.tracking_flag.load(Ordering::Acquire)
|
self.tracking.load(Ordering::Acquire)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a weak reference to a room by its ID.
|
/// Get a weak reference to a room by its ID.
|
||||||
@@ -491,7 +492,7 @@ impl ChatRegistry {
|
|||||||
self.event_map
|
self.event_map
|
||||||
.read_blocking()
|
.read_blocking()
|
||||||
.get(id)
|
.get(id)
|
||||||
.map(|(id, _dekey)| self.seen_on(id))
|
.map(|id| self.seen_on(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the relays that have seen a given gift wrap id.
|
/// Get the relays that have seen a given gift wrap id.
|
||||||
@@ -503,15 +504,6 @@ impl ChatRegistry {
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a given rumor was encrypted by the dekey.
|
|
||||||
pub fn encrypted_by_dekey(&self, id: &EventId) -> bool {
|
|
||||||
self.event_map
|
|
||||||
.read_blocking()
|
|
||||||
.get(id)
|
|
||||||
.map(|(_, dekey)| *dekey)
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a new room to the start of list.
|
/// Add a new room to the start of list.
|
||||||
pub fn add_room<I>(&mut self, room: I, cx: &mut Context<Self>)
|
pub fn add_room<I>(&mut self, room: I, cx: &mut Context<Self>)
|
||||||
where
|
where
|
||||||
|
|||||||
@@ -470,12 +470,6 @@ impl ChatPanel {
|
|||||||
self.reports_by_id.read(cx).get(id).is_some()
|
self.reports_by_id.read(cx).get(id).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a message was encrypted by the dekey
|
|
||||||
fn encrypted_by_dekey(&self, id: &EventId, cx: &App) -> bool {
|
|
||||||
let chat = ChatRegistry::global(cx);
|
|
||||||
chat.read(cx).encrypted_by_dekey(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get all sent reports for a message by its ID
|
/// Get all sent reports for a message by its ID
|
||||||
fn sent_reports(&self, id: &EventId, cx: &App) -> Option<Vec<SendReport>> {
|
fn sent_reports(&self, id: &EventId, cx: &App) -> Option<Vec<SendReport>> {
|
||||||
self.reports_by_id.read(cx).get(id).cloned()
|
self.reports_by_id.read(cx).get(id).cloned()
|
||||||
@@ -848,7 +842,6 @@ impl ChatPanel {
|
|||||||
let replies = message.replies_to.as_slice();
|
let replies = message.replies_to.as_slice();
|
||||||
let has_replies = !replies.is_empty();
|
let has_replies = !replies.is_empty();
|
||||||
let has_reports = self.has_reports(&id, cx);
|
let has_reports = self.has_reports(&id, cx);
|
||||||
let encrypted_by_dekey = self.encrypted_by_dekey(&id, cx);
|
|
||||||
|
|
||||||
// Hide avatar setting
|
// Hide avatar setting
|
||||||
let hide_avatar = AppSettings::get_hide_avatar(cx);
|
let hide_avatar = AppSettings::get_hide_avatar(cx);
|
||||||
@@ -894,17 +887,6 @@ impl ChatPanel {
|
|||||||
.text_color(cx.theme().text)
|
.text_color(cx.theme().text)
|
||||||
.child(author.name()),
|
.child(author.name()),
|
||||||
)
|
)
|
||||||
.when(encrypted_by_dekey, |this| {
|
|
||||||
this.child(
|
|
||||||
Button::new(format!("dekey-{ix}"))
|
|
||||||
.icon(IconName::Shield)
|
|
||||||
.ghost()
|
|
||||||
.xsmall()
|
|
||||||
.px_4()
|
|
||||||
.tooltip("Encrypted by Dekey")
|
|
||||||
.disabled(true),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.child(message.created_at.to_human_time())
|
.child(message.created_at.to_human_time())
|
||||||
.when(has_reports, |this| {
|
.when(has_reports, |this| {
|
||||||
this.child(deferred(self.render_sent_reports(&id, cx)))
|
this.child(deferred(self.render_sent_reports(&id, cx)))
|
||||||
|
|||||||
@@ -298,10 +298,10 @@ impl Render for Notification {
|
|||||||
|
|
||||||
let action = self.action_builder.clone().map(|builder| {
|
let action = self.action_builder.clone().map(|builder| {
|
||||||
builder(self, window, cx)
|
builder(self, window, cx)
|
||||||
.xsmall()
|
.small()
|
||||||
.primary()
|
.primary()
|
||||||
.px_3()
|
.px_4()
|
||||||
.font_semibold()
|
.font_medium()
|
||||||
});
|
});
|
||||||
|
|
||||||
let icon = match self.kind {
|
let icon = match self.kind {
|
||||||
@@ -364,8 +364,14 @@ impl Render for Notification {
|
|||||||
})
|
})
|
||||||
.when_some(content, |this, content| this.child(content))
|
.when_some(content, |this, content| this.child(content))
|
||||||
.when_some(action, |this, action| {
|
.when_some(action, |this, action| {
|
||||||
this.gap_2()
|
this.gap_2().child(
|
||||||
.child(h_flex().w_full().flex_1().justify_end().child(action))
|
h_flex()
|
||||||
|
.mt_2()
|
||||||
|
.w_full()
|
||||||
|
.flex_1()
|
||||||
|
.justify_end()
|
||||||
|
.child(action),
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ pub fn init(window: &mut Window, cx: &mut App) -> Entity<Workspace> {
|
|||||||
struct DeviceNotifcation;
|
struct DeviceNotifcation;
|
||||||
struct SignerNotifcation;
|
struct SignerNotifcation;
|
||||||
struct RelayNotifcation;
|
struct RelayNotifcation;
|
||||||
|
struct MsgRelayNotification;
|
||||||
|
|
||||||
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
|
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
|
||||||
#[action(namespace = workspace, no_json)]
|
#[action(namespace = workspace, no_json)]
|
||||||
@@ -184,6 +185,27 @@ impl Workspace {
|
|||||||
// Observe all events emitted by the chat registry
|
// Observe all events emitted by the chat registry
|
||||||
cx.subscribe_in(&chat, window, move |this, chat, ev, window, cx| {
|
cx.subscribe_in(&chat, window, move |this, chat, ev, window, cx| {
|
||||||
match ev {
|
match ev {
|
||||||
|
ChatEvent::InboxRelayNotFound => {
|
||||||
|
const MSG: &str = "Messaging Relays not found. Cannot receive messages.";
|
||||||
|
|
||||||
|
window.push_notification(
|
||||||
|
Notification::warning(MSG)
|
||||||
|
.id::<MsgRelayNotification>()
|
||||||
|
.autohide(false)
|
||||||
|
.action(|_this, _window, _cx| {
|
||||||
|
Button::new("retry").label("Retry").on_click(
|
||||||
|
move |_this, window, cx| {
|
||||||
|
let chat = ChatRegistry::global(cx);
|
||||||
|
chat.update(cx, |this, cx| {
|
||||||
|
this.get_metadata(cx);
|
||||||
|
});
|
||||||
|
window.clear_notification::<MsgRelayNotification>(cx);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}
|
||||||
ChatEvent::OpenRoom(id) => {
|
ChatEvent::OpenRoom(id) => {
|
||||||
if let Some(room) = chat.read(cx).room(id, cx) {
|
if let Some(room) = chat.read(cx).room(id, cx) {
|
||||||
this.dock.update(cx, |this, cx| {
|
this.dock.update(cx, |this, cx| {
|
||||||
|
|||||||
Reference in New Issue
Block a user