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 81 additions and 80 deletions
Showing only changes of commit 707533c145 - Show all commits

View File

@@ -7,6 +7,7 @@ use std::time::Duration;
use anyhow::{Context as AnyhowContext, Error, anyhow}; use anyhow::{Context as AnyhowContext, Error, anyhow};
use common::EventUtils; use common::EventUtils;
use device::{DeviceEvent, DeviceRegistry};
use fuzzy_matcher::FuzzyMatcher; use fuzzy_matcher::FuzzyMatcher;
use fuzzy_matcher::skim::SkimMatcherV2; use fuzzy_matcher::skim::SkimMatcherV2;
use gpui::{ use gpui::{
@@ -104,7 +105,7 @@ pub struct ChatRegistry {
tasks: SmallVec<[Task<Result<(), Error>>; 2]>, tasks: SmallVec<[Task<Result<(), Error>>; 2]>,
/// Subscriptions /// Subscriptions
_subscriptions: SmallVec<[Subscription; 1]>, _subscriptions: SmallVec<[Subscription; 2]>,
} }
impl EventEmitter<ChatEvent> for ChatRegistry {} impl EventEmitter<ChatEvent> for ChatRegistry {}
@@ -123,17 +124,48 @@ impl ChatRegistry {
/// Create a new chat registry instance /// Create a new chat registry instance
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self { fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
let nostr = NostrRegistry::global(cx); let nostr = NostrRegistry::global(cx);
let device = DeviceRegistry::global(cx);
let (tx, rx) = flume::unbounded::<Signal>(); let (tx, rx) = flume::unbounded::<Signal>();
let mut subscriptions = smallvec![]; let mut subscriptions = smallvec![];
subscriptions.push( subscriptions.push(
// Subscribe to the signer event // Subscribe to the signer event
cx.subscribe(&nostr, |this, _state, event, cx| { cx.subscribe_in(&nostr, window, |this, state, event, window, cx| {
if event == &StateEvent::SignerSet { if event == &StateEvent::SignerSet {
this.reset(cx); this.reset(cx);
this.get_contact_list(cx); this.get_contact_list(cx);
this.get_messages(cx);
this.get_rooms(cx); this.get_rooms(cx);
let signer = state.read(cx).signer();
cx.spawn_in(window, async move |this, cx| {
let user_signer = signer.get().await;
this.update(cx, |this, cx| {
this.get_messages(user_signer, cx);
})
.ok();
})
.detach();
};
}),
);
subscriptions.push(
// Subscribe to the device event
cx.subscribe_in(&device, window, |_this, _s, event, window, cx| {
if event == &DeviceEvent::Set {
let nostr = NostrRegistry::global(cx);
let signer = nostr.read(cx).signer();
cx.spawn_in(window, async move |this, cx| {
if let Some(device_signer) = signer.get_encryption_signer().await {
this.update(cx, |this, cx| {
this.get_messages(device_signer, cx);
})
.ok();
}
})
.detach();
}; };
}), }),
); );
@@ -297,7 +329,7 @@ impl ChatRegistry {
} }
/// Get contact list from relays /// Get contact list from relays
pub fn get_contact_list(&mut self, cx: &mut Context<Self>) { fn get_contact_list(&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();
@@ -327,9 +359,12 @@ impl ChatRegistry {
self.tasks.push(task); self.tasks.push(task);
} }
/// Get all messages for current user /// Get all messages for the provided signer
pub fn get_messages(&mut self, cx: &mut Context<Self>) { fn get_messages<T>(&mut self, signer: T, cx: &mut Context<Self>)
let task = self.subscribe_gift_wrap_events(cx); where
T: NostrSigner + 'static,
{
let task = self.subscribe_gift_wrap_events(signer, cx);
self.tasks.push(cx.spawn(async move |this, cx| { self.tasks.push(cx.spawn(async move |this, cx| {
match task.await { match task.await {
@@ -382,18 +417,20 @@ impl ChatRegistry {
}) })
} }
/// Continuously get gift wrap events for the current user in their messaging relays /// Continuously get gift wrap events for the signer
fn subscribe_gift_wrap_events(&self, cx: &App) -> Task<Result<(), Error>> { fn subscribe_gift_wrap_events<T>(&self, signer: T, cx: &App) -> Task<Result<(), Error>>
where
T: NostrSigner + 'static,
{
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 urls = self.get_messaging_relays(cx); let urls = self.get_messaging_relays(cx);
cx.background_spawn(async move { cx.background_spawn(async move {
let urls = urls.await?; let urls = urls.await?;
let public_key = signer.get_public_key().await?; let public_key = signer.get_public_key().await?;
let filter = Filter::new().kind(Kind::GiftWrap).pubkey(public_key); let filter = Filter::new().kind(Kind::GiftWrap).pubkey(public_key);
let id = SubscriptionId::new(USER_GIFTWRAP); let id = SubscriptionId::new(format!("{}-msg", public_key.to_hex()));
// Ensure relay connections // Ensure relay connections
for url in urls.iter() { for url in urls.iter() {
@@ -417,6 +454,31 @@ impl ChatRegistry {
}) })
} }
/// Refresh the chat registry, fetching messages and contact list from relays.
pub fn refresh(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.reset(cx);
self.get_contact_list(cx);
self.get_rooms(cx);
let nostr = NostrRegistry::global(cx);
let signer = nostr.read(cx).signer();
cx.spawn_in(window, async move |this, cx| {
let user_signer = signer.get().await;
let device_signer = signer.get_encryption_signer().await;
this.update(cx, |this, cx| {
this.get_messages(user_signer, cx);
if let Some(device_signer) = device_signer {
this.get_messages(device_signer, cx);
}
})
.ok();
})
.detach();
}
/// Set the initializing status of the chat registry /// Set the initializing status of the chat registry
fn set_initializing(&mut self, initializing: bool, cx: &mut Context<Self>) { fn set_initializing(&mut self, initializing: bool, cx: &mut Context<Self>) {
self.initializing = initializing; self.initializing = initializing;

View File

@@ -92,14 +92,14 @@ fn main() {
// Initialize relay auth registry // Initialize relay auth registry
relay_auth::init(window, cx); relay_auth::init(window, cx);
// Initialize app registry
chat::init(window, cx);
// Initialize device signer // Initialize device signer
// //
// NIP-4e: https://github.com/nostr-protocol/nips/blob/per-device-keys/4e.md // NIP-4e: https://github.com/nostr-protocol/nips/blob/per-device-keys/4e.md
device::init(window, cx); device::init(window, cx);
// Initialize app registry
chat::init(window, cx);
// Initialize auto update // Initialize auto update
auto_update::init(window, cx); auto_update::init(window, cx);

View File

@@ -354,8 +354,9 @@ impl Workspace {
} }
Command::RefreshMessagingRelays => { Command::RefreshMessagingRelays => {
let chat = ChatRegistry::global(cx); let chat = ChatRegistry::global(cx);
// Trigger a refresh of the chat registry
chat.update(cx, |this, cx| { chat.update(cx, |this, cx| {
this.get_messages(cx); this.refresh(window, cx);
}); });
} }
Command::ShowRelayList => { Command::ShowRelayList => {

View File

@@ -1,5 +1,5 @@
use std::cell::Cell; use std::cell::Cell;
use std::collections::{HashMap, HashSet}; use std::collections::HashSet;
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
use std::time::Duration; use std::time::Duration;
@@ -11,7 +11,7 @@ use gpui::{
}; };
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use person::PersonRegistry; use person::PersonRegistry;
use state::{Announcement, DEVICE_GIFTWRAP, NostrRegistry, StateEvent, TIMEOUT, app_name}; use state::{Announcement, NostrRegistry, StateEvent, TIMEOUT, app_name};
use theme::ActiveTheme; use theme::ActiveTheme;
use ui::avatar::Avatar; use ui::avatar::Avatar;
use ui::button::{Button, ButtonVariants}; use ui::button::{Button, ButtonVariants};
@@ -213,76 +213,14 @@ impl DeviceRegistry {
// Update state // Update state
this.update(cx, |this, cx| { this.update(cx, |this, cx| {
this.set_initializing(false, cx);
cx.emit(DeviceEvent::Set); cx.emit(DeviceEvent::Set);
this.get_messages(cx);
})?; })?;
Ok(()) Ok(())
})); }));
} }
/// Get all messages for encryption keys
fn get_messages(&mut self, cx: &mut Context<Self>) {
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::error(e.to_string()));
})?;
} else {
this.update(cx, |this, cx| {
this.set_initializing(false, cx);
})?;
}
Ok(())
}));
}
/// Continuously get gift wrap events for the current user in their messaging relays
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();
let signer = nostr.read(cx).signer();
let Some(user) = signer.public_key() else {
return Task::ready(Err(anyhow!("User not found")));
};
let profile = persons.read(cx).get(&user, cx);
let relays = profile.messaging_relays().clone();
cx.background_spawn(async move {
let encryption = signer.get_encryption_signer().await.context("not found")?;
let public_key = encryption.get_public_key().await?;
let filter = Filter::new().kind(Kind::GiftWrap).pubkey(public_key);
let id = SubscriptionId::new(DEVICE_GIFTWRAP);
// Ensure user has relays configured
if relays.is_empty() {
return Err(anyhow!("No messaging relays found"));
}
// Ensure relays are connected
for url in relays.iter() {
client.add_relay(url).and_connect().await?;
}
// Construct target for subscription
let target: HashMap<RelayUrl, Filter> = relays
.into_iter()
.map(|relay| (relay, filter.clone()))
.collect();
// Subscribe
client.subscribe(target).with_id(id).await?;
Ok(())
})
}
/// Backup the encryption's secret key to a file /// Backup the encryption's secret key to a file
pub fn backup(&self, path: PathBuf, cx: &App) -> Task<Result<(), Error>> { pub fn backup(&self, path: PathBuf, cx: &App) -> Task<Result<(), Error>> {
let nostr = NostrRegistry::global(cx); let nostr = NostrRegistry::global(cx);