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