chore: simplify codebase and prepare for multi-platforms #28

Merged
reya merged 11 commits from improve-codebase into master 2026-04-04 02:22:08 +00:00
4 changed files with 67 additions and 108 deletions
Showing only changes of commit 163865eb46 - Show all commits

View File

@@ -41,8 +41,6 @@ pub enum ChatEvent {
CloseRoom(u64),
/// An event to notify UI about a new chat request
Ping,
/// An event to notify UI that the chat registry has subscribed to messaging relays
Subscribed,
/// An error occurred
Error(SharedString),
}
@@ -78,6 +76,9 @@ impl Signal {
/// Chat Registry
#[derive(Debug)]
pub struct ChatRegistry {
/// Whether the chat registry is currently initializing.
pub initializing: bool,
/// Chat rooms
rooms: Vec<Entity<Room>>,
@@ -130,9 +131,9 @@ impl ChatRegistry {
cx.subscribe(&nostr, |this, _state, event, cx| {
if event == &StateEvent::SignerSet {
this.reset(cx);
this.get_rooms(cx);
this.get_contact_list(cx);
this.get_messages(cx);
this.get_rooms(cx);
};
}),
);
@@ -145,6 +146,7 @@ impl ChatRegistry {
});
Self {
initializing: true,
rooms: vec![],
trashes: cx.new(|_| BTreeSet::default()),
seens: Arc::new(RwLock::new(HashMap::default())),
@@ -327,13 +329,13 @@ impl ChatRegistry {
/// Get all messages for current user
pub fn get_messages(&mut self, cx: &mut Context<Self>) {
let task = self.subscribe(cx);
let task = self.subscribe_gift_wrap_events(cx);
self.tasks.push(cx.spawn(async move |this, cx| {
match task.await {
Ok(_) => {
this.update(cx, |_this, cx| {
cx.emit(ChatEvent::Subscribed);
this.update(cx, |this, cx| {
this.set_initializing(false, cx);
})?;
}
Err(e) => {
@@ -354,6 +356,7 @@ impl ChatRegistry {
cx.background_spawn(async move {
let public_key = signer.get_public_key().await?;
let id = SubscriptionId::new("inbox-relay");
// Construct filter for inbox relays
let filter = Filter::new()
@@ -364,12 +367,12 @@ impl ChatRegistry {
// Stream events from user's write relays
let mut stream = client
.stream_events(filter)
.with_id(id)
.timeout(Duration::from_secs(TIMEOUT))
.await?;
while let Some((_url, res)) = stream.next().await {
if let Ok(event) = res {
log::debug!("Got event: {:?}", event);
let urls: Vec<RelayUrl> = nip17::extract_owned_relay_list(event).collect();
return Ok(urls);
}
@@ -380,7 +383,7 @@ impl ChatRegistry {
}
/// Continuously get gift wrap events for the current user in their messaging relays
fn subscribe(&self, cx: &App) -> Task<Result<(), Error>> {
fn subscribe_gift_wrap_events(&self, cx: &App) -> Task<Result<(), Error>> {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let signer = nostr.read(cx).signer();
@@ -414,6 +417,12 @@ impl ChatRegistry {
})
}
/// Set the initializing status of the chat registry
fn set_initializing(&mut self, initializing: bool, cx: &mut Context<Self>) {
self.initializing = initializing;
cx.notify();
}
/// Get the loading status of the chat registry
pub fn loading(&self) -> bool {
self.tracking_flag.load(Ordering::Acquire)
@@ -557,7 +566,12 @@ impl ChatRegistry {
/// Reset the registry.
pub fn reset(&mut self, cx: &mut Context<Self>) {
self.initializing = true;
self.rooms.clear();
self.trashes.update(cx, |this, cx| {
this.clear();
cx.notify();
});
cx.notify();
}

View File

@@ -177,16 +177,6 @@ impl Workspace {
window.push_notification(note, cx);
}
DeviceEvent::NotSubscribe { reason } => {
let note = Notification::new()
.id::<DeviceNotifcation>()
.title("Cannot getting messages")
.message(reason)
.autohide(false)
.with_kind(NotificationKind::Error);
window.push_notification(note, cx);
}
DeviceEvent::Error(error) => {
window.push_notification(Notification::error(error).autohide(false), cx);
}
@@ -650,6 +640,12 @@ impl Workspace {
}
fn titlebar_right(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
let chat = ChatRegistry::global(cx);
let initializing = chat.read(cx).initializing;
let device = DeviceRegistry::global(cx);
let device_initializing = device.read(cx).initializing;
let nostr = NostrRegistry::global(cx);
let signer = nostr.read(cx).signer();
@@ -701,9 +697,13 @@ impl Workspace {
.tooltip("Decoupled encryption key")
.small()
.ghost()
.loading(device_initializing)
.when(device_initializing, |this| {
this.label("Dekey")
.xsmall()
.tooltip("Loading decoupled encryption key...")
})
.dropdown_menu(move |this, _window, cx| {
let device = DeviceRegistry::global(cx);
let subscribing = device.read(cx).subscribing;
let requesting = device.read(cx).requesting;
this.min_w(px(260.))
@@ -724,30 +724,6 @@ impl Workspace {
.child(SharedString::from("Waiting for approval..."))
}))
})
.item(PopupMenuItem::element(move |_window, cx| {
h_flex()
.px_1()
.w_full()
.gap_2()
.text_sm()
.when(!subscribing, |this| {
this.text_color(cx.theme().text_muted)
})
.child(div().size_1p5().rounded_full().map(|this| {
if subscribing {
this.bg(cx.theme().icon_accent)
} else {
this.bg(cx.theme().icon_muted)
}
}))
.map(|this| {
if subscribing {
this.child("Listening for messages")
} else {
this.child("Idle")
}
})
}))
.separator()
.menu_with_icon(
"Backup",
@@ -777,8 +753,13 @@ impl Workspace {
.icon(IconName::Inbox)
.small()
.ghost()
.loading(initializing)
.when(initializing, |this| {
this.label("Inbox")
.xsmall()
.tooltip("Getting inbox messages...")
})
.dropdown_menu(move |this, _window, cx| {
let chat = ChatRegistry::global(cx);
let persons = PersonRegistry::global(cx);
let profile = persons.read(cx).get(&public_key, cx);
@@ -832,12 +813,12 @@ impl Workspace {
Box::new(Command::RefreshMessagingRelays),
)
.menu_with_icon(
"Update gossip relays",
"Manage gossip relays",
IconName::Relay,
Box::new(Command::ShowRelayList),
)
.menu_with_icon(
"Update messaging relays",
"Manage messaging relays",
IconName::Settings,
Box::new(Command::ShowMessaging),
)

View File

@@ -41,8 +41,6 @@ pub enum DeviceEvent {
Creating,
/// Encryption key is not set
NotSet { reason: SharedString },
/// An event to notify that Coop isn't subscribed to gift wrap events
NotSubscribe { reason: SharedString },
/// An error occurred
Error(SharedString),
}
@@ -55,15 +53,6 @@ impl DeviceEvent {
Self::Error(error.into())
}
pub fn not_subscribe<T>(reason: T) -> Self
where
T: Into<SharedString>,
{
Self::NotSubscribe {
reason: reason.into(),
}
}
pub fn not_set<T>(reason: T) -> Self
where
T: Into<SharedString>,
@@ -79,14 +68,14 @@ impl DeviceEvent {
/// NIP-4e: https://github.com/nostr-protocol/nips/blob/per-device-keys/4e.md
#[derive(Debug)]
pub struct DeviceRegistry {
/// Whether the registry is currently subscribing to gift wrap events
pub subscribing: bool,
/// Whether the registry is currently initializing
pub initializing: bool,
/// Whether the registry is waiting for encryption key approval from other devices
pub requesting: bool,
/// Whether there is a pending request for encryption key approval
pub has_pending_request: bool,
pub pending_request: bool,
/// Async tasks
tasks: Vec<Task<Result<(), Error>>>,
@@ -111,10 +100,13 @@ impl DeviceRegistry {
/// Create a new device registry instance
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
let nostr = NostrRegistry::global(cx);
// Subscribe to nostr state events
let subscription = cx.subscribe_in(&nostr, window, |this, _e, event, _window, cx| {
if event == &StateEvent::SignerSet {
this.set_subscribing(false, cx);
this.set_initializing(true, cx);
this.set_requesting(false, cx);
this.get_announcement(cx);
};
});
@@ -123,9 +115,9 @@ impl DeviceRegistry {
});
Self {
subscribing: false,
initializing: true,
requesting: false,
has_pending_request: false,
pending_request: false,
tasks: vec![],
_subscription: Some(subscription),
}
@@ -139,7 +131,6 @@ impl DeviceRegistry {
self.tasks.push(cx.background_spawn(async move {
let mut notifications = client.notifications();
let mut processed_events = HashSet::new();
let mut found_relay_list = false;
while let Some(notification) = notifications.next().await {
if let ClientNotification::Message { message, .. } = notification
@@ -151,17 +142,6 @@ impl DeviceRegistry {
}
match event.kind {
Kind::RelayList => {
// Skip if the relay list has already been found
if found_relay_list {
continue;
}
// Verify the relay list event is signed by the user's signer
if verify_author(&client, event.as_ref()).await {
tx.send_async(event.into_owned()).await?;
found_relay_list = true;
}
}
Kind::Custom(4454) => {
if verify_author(&client, event.as_ref()).await {
tx.send_async(event.into_owned()).await?;
@@ -202,9 +182,9 @@ impl DeviceRegistry {
}));
}
/// Set whether the registry is currently subscribing to gift wrap events
fn set_subscribing(&mut self, subscribing: bool, cx: &mut Context<Self>) {
self.subscribing = subscribing;
/// Set whether the registry is currently initializing
fn set_initializing(&mut self, initializing: bool, cx: &mut Context<Self>) {
self.initializing = initializing;
cx.notify();
}
@@ -215,8 +195,8 @@ impl DeviceRegistry {
}
/// Set whether there is a pending request for encryption key approval
fn set_has_pending_request(&mut self, pending: bool, cx: &mut Context<Self>) {
self.has_pending_request = pending;
fn set_pending_request(&mut self, pending: bool, cx: &mut Context<Self>) {
self.pending_request = pending;
cx.notify();
}
@@ -243,16 +223,16 @@ impl DeviceRegistry {
/// Get all messages for encryption keys
fn get_messages(&mut self, cx: &mut Context<Self>) {
let task = self.subscribe_to_giftwrap_events(cx);
let task = self.subscribe_gift_wrap_events(cx);
self.tasks.push(cx.spawn(async move |this, cx| {
if let Err(e) = task.await {
this.update(cx, |_this, cx| {
cx.emit(DeviceEvent::not_subscribe(e.to_string()));
cx.emit(DeviceEvent::error(e.to_string()));
})?;
} else {
this.update(cx, |this, cx| {
this.set_subscribing(true, cx);
this.set_initializing(false, cx);
})?;
}
Ok(())
@@ -260,7 +240,7 @@ impl DeviceRegistry {
}
/// Continuously get gift wrap events for the current user in their messaging relays
fn subscribe_to_giftwrap_events(&self, cx: &App) -> Task<Result<(), Error>> {
fn subscribe_gift_wrap_events(&self, cx: &App) -> Task<Result<(), Error>> {
let persons = PersonRegistry::global(cx);
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
@@ -471,21 +451,16 @@ impl DeviceRegistry {
self.tasks.push(cx.background_spawn(async move {
let public_key = signer.get_public_key().await?;
let id = SubscriptionId::new("dekey-requests");
// Construct a filter for encryption key requests
let now = Filter::new()
let filter = Filter::new()
.kind(Kind::Custom(4454))
.author(public_key)
.since(Timestamp::now());
// Construct a filter for the last encryption key request
let last = Filter::new()
.kind(Kind::Custom(4454))
.author(public_key)
.limit(1);
// Subscribe to the device key requests on user's write relays
client.subscribe(vec![now, last]).await?;
client.subscribe(vec![filter]).with_id(id).await?;
Ok(())
}));
@@ -687,10 +662,10 @@ impl DeviceRegistry {
/// Handle encryption request
fn ask_for_approval(&mut self, event: Event, window: &mut Window, cx: &mut Context<Self>) {
// Ignore if there is already a pending request
if self.has_pending_request {
if self.pending_request {
return;
}
self.set_has_pending_request(true, cx);
self.set_pending_request(true, cx);
// Show notification
let notification = self.notification(event, cx);

View File

@@ -133,14 +133,7 @@ impl NostrRegistry {
.signer(signer.clone())
.database(lmdb)
.gossip(NostrGossipMemory::unbounded())
.gossip_config(
GossipConfig::default()
.no_background_refresh()
.sync_idle_timeout(Duration::from_secs(TIMEOUT))
.sync_initial_timeout(Duration::from_millis(600)),
)
.automatic_authentication(false)
.verify_subscriptions(true)
.connect_timeout(Duration::from_secs(10))
.sleep_when_idle(SleepWhenIdle::Enabled {
timeout: Duration::from_secs(600),
@@ -280,7 +273,7 @@ impl NostrRegistry {
let app_keys = self.app_keys.clone();
if let Ok(payload) = std::fs::read_to_string(key_path) {
if payload.starts_with("nsec1") || payload.starts_with("bunker://") {
if !payload.is_empty() {
cx.background_spawn(async move {
let decrypted = app_keys.nip44_decrypt(&public_key, &payload).await?;
let secret = SecretKey::parse(&decrypted)?;
@@ -435,11 +428,7 @@ impl NostrRegistry {
let event = EventBuilder::nip17_relay_list(relays).sign(&signer).await?;
// Publish messaging relay list event
client
.send_event(&event)
.to_nip65()
.ack_policy(AckPolicy::none())
.await?;
client.send_event(&event).to_nip65().await?;
// Write user's credentials to the system keyring
write_secret.await?;