commit b0f187fee6732628b481cbfa85732e8ea95a3154
parent 4922bcd48b52377ff8b1ef20ab292872f9c5a161
Author: kernelkind <kernelkind@gmail.com>
Date: Wed, 25 Feb 2026 19:27:27 -0500
feat(outbox-int): migrate accounts to use outbox
Signed-off-by: kernelkind <kernelkind@gmail.com>
Diffstat:
8 files changed, 308 insertions(+), 348 deletions(-)
diff --git a/crates/notedeck/src/account/accounts.rs b/crates/notedeck/src/account/accounts.rs
@@ -1,22 +1,22 @@
-use hashbrown::HashSet;
-use uuid::Uuid;
-
use crate::account::cache::AccountCache;
use crate::account::contacts::Contacts;
use crate::account::mute::AccountMutedData;
use crate::account::relay::{
- modify_advertised_relays, update_relay_configuration, AccountRelayData, RelayAction,
+ calculate_relays, modify_advertised_relays, write_relays, AccountRelayData, RelayAction,
RelayDefaults,
};
+use crate::scoped_subs::{RelaySelection, ScopedSubIdentity, SubConfig, SubKey};
use crate::storage::AccountStorageWriter;
use crate::user_account::UserAccountSerializable;
use crate::{
- AccountStorage, MuteFun, SingleUnkIdAction, UnifiedSubscription, UnknownIds, UserAccount,
- ZapWallet,
+ AccountStorage, MuteFun, RemoteApi, ScopedSubApi, SingleUnkIdAction, SubOwnerKey, UnknownIds,
+ UserAccount, ZapWallet,
};
-use enostr::{ClientMessage, FilledKeypair, Keypair, NormRelayUrl, Pubkey, RelayId, RelayPool};
-use nostrdb::{Ndb, Note, Transaction};
+use enostr::{FilledKeypair, Keypair, NormRelayUrl, Pubkey, RelayId};
+use hashbrown::HashSet;
+use nostrdb::{Filter, Ndb, Note, Subscription, Transaction};
+use std::slice::from_ref;
// TODO: remove this
use std::sync::Arc;
@@ -26,7 +26,8 @@ pub struct Accounts {
pub cache: AccountCache,
storage_writer: Option<AccountStorageWriter>,
relay_defaults: RelayDefaults,
- subs: AccountSubs,
+ ndb_subs: AccountNdbSubs,
+ scoped_remote_initialized: bool,
}
impl Accounts {
@@ -37,8 +38,6 @@ impl Accounts {
fallback: Pubkey,
ndb: &mut Ndb,
txn: &Transaction,
- pool: &mut RelayPool,
- ctx: &egui::Context,
unknown_ids: &mut UnknownIds,
) -> Self {
let (mut cache, unknown_id) = AccountCache::new(UserAccount::new(
@@ -79,31 +78,31 @@ impl Accounts {
selected_data.query(ndb, txn);
- let subs = {
- AccountSubs::new(
- ndb,
- pool,
- &relay_defaults,
- &selected.key.pubkey,
- selected_data,
- create_wakeup(ctx),
- )
- };
+ let ndb_subs = AccountNdbSubs::new(ndb, selected_data);
Accounts {
cache,
storage_writer,
relay_defaults,
- subs,
+ ndb_subs,
+ scoped_remote_initialized: false,
}
}
- pub fn remove_account(
+ pub(crate) fn remove_account(
+ &mut self,
+ pk: &Pubkey,
+ ndb: &mut Ndb,
+ remote: &mut RemoteApi<'_>,
+ ) -> bool {
+ self.remove_account_internal(pk, ndb, remote)
+ }
+
+ fn remove_account_internal(
&mut self,
pk: &Pubkey,
ndb: &mut Ndb,
- pool: &mut RelayPool,
- ctx: &egui::Context,
+ remote: &mut RemoteApi<'_>,
) -> bool {
let Some(resp) = self.cache.remove(pk) else {
return false;
@@ -118,8 +117,14 @@ impl Accounts {
}
if let Some(swap_to) = resp.swap_to {
+ let old_pk = resp.deleted.pubkey;
let txn = Transaction::new(ndb).expect("txn");
- self.select_account_internal(&swap_to, ndb, &txn, pool, ctx);
+ self.select_account_internal(&swap_to, old_pk, ndb, &txn, remote);
+ }
+
+ {
+ let mut scoped_subs = remote.scoped_subs(&*self);
+ clear_account_remote_subs_for_account(&mut scoped_subs, resp.deleted.pubkey);
}
true
@@ -220,29 +225,40 @@ impl Accounts {
&self.cache.selected().data
}
- pub fn select_account(
+ pub(crate) fn select_account(
&mut self,
pk_to_select: &Pubkey,
ndb: &mut Ndb,
txn: &Transaction,
- pool: &mut RelayPool,
- ctx: &egui::Context,
+ remote: &mut RemoteApi<'_>,
) {
+ self.select_account_internal_entry(pk_to_select, ndb, txn, remote);
+ }
+
+ fn select_account_internal_entry(
+ &mut self,
+ pk_to_select: &Pubkey,
+ ndb: &mut Ndb,
+ txn: &Transaction,
+ remote: &mut RemoteApi<'_>,
+ ) {
+ let old_pk = *self.selected_account_pubkey();
+
if !self.cache.select(*pk_to_select) {
return;
}
- self.select_account_internal(pk_to_select, ndb, txn, pool, ctx);
+ self.select_account_internal(pk_to_select, old_pk, ndb, txn, remote);
}
/// Have already selected in `AccountCache`, updating other things
fn select_account_internal(
&mut self,
pk_to_select: &Pubkey,
+ old_pk: Pubkey,
ndb: &mut Ndb,
txn: &Transaction,
- pool: &mut RelayPool,
- ctx: &egui::Context,
+ remote: &mut RemoteApi<'_>,
) {
if let Some(key_store) = &self.storage_writer {
if let Err(e) = key_store.select_key(Some(*pk_to_select)) {
@@ -251,14 +267,11 @@ impl Accounts {
}
self.get_selected_account_mut().data.query(ndb, txn);
- self.subs.swap_to(
- ndb,
- pool,
- &self.relay_defaults,
- pk_to_select,
- &self.cache.selected().data,
- create_wakeup(ctx),
- );
+ self.ndb_subs.swap_to(ndb, &self.cache.selected().data);
+
+ remote.on_account_switched(old_pk, *pk_to_select, self);
+
+ self.ensure_selected_account_remote_subs(remote);
}
pub fn mutefun(&self) -> Box<MuteFun> {
@@ -279,106 +292,52 @@ impl Accounts {
}
}
- pub fn send_initial_filters(&mut self, pool: &mut RelayPool, relay_url: &str) {
- let data = &self.get_selected_account().data;
- // send the active account's relay list subscription
- pool.send_to(
- &ClientMessage::req(
- self.subs.relay.remote.clone(),
- vec![data.relay.filter.clone()],
- ),
- relay_url,
- );
- // send the active account's muted subscription
- pool.send_to(
- &ClientMessage::req(
- self.subs.mute.remote.clone(),
- vec![data.muted.filter.clone()],
- ),
- relay_url,
- );
- pool.send_to(
- &ClientMessage::req(
- self.subs.contacts.remote.clone(),
- vec![data.contacts.filter.clone()],
- ),
- relay_url,
- );
- if let Some(cur_pk) = self.selected_filled().map(|s| s.pubkey) {
- let giftwraps_filter = nostrdb::Filter::new()
- .kinds([1059])
- .pubkeys([cur_pk.bytes()])
- .build();
- pool.send_to(
- &ClientMessage::req(self.subs.giftwraps.remote.clone(), vec![giftwraps_filter]),
- relay_url,
- );
- }
- }
-
- pub fn update(&mut self, ndb: &mut Ndb, pool: &mut RelayPool, ctx: &egui::Context) {
+ #[profiling::function]
+ pub fn update(&mut self, ndb: &mut Ndb, remote: &mut RemoteApi<'_>) {
// IMPORTANT - This function is called in the UI update loop,
// make sure it is fast when idle
- let Some(update) = self
+ let relay_updated = self
.cache
.selected_mut()
.data
- .poll_for_updates(ndb, &self.subs)
- else {
+ .poll_for_updates(ndb, &self.ndb_subs);
+
+ if !self.scoped_remote_initialized {
+ self.ensure_selected_account_remote_subs(remote);
return;
- };
+ }
- match update {
- // If needed, update the relay configuration
- AccountDataUpdate::Relay => {
- let acc = self.cache.selected();
- update_relay_configuration(
- pool,
- &self.relay_defaults,
- &acc.key.pubkey,
- &acc.data.relay,
- create_wakeup(ctx),
- );
- }
+ if !relay_updated {
+ return;
}
+
+ self.retarget_selected_account_read_relays(remote);
}
pub fn get_full<'a>(&'a self, pubkey: &Pubkey) -> Option<FilledKeypair<'a>> {
self.cache.get(pubkey).and_then(|r| r.key.to_full())
}
- pub fn process_relay_action(
- &mut self,
- ctx: &egui::Context,
- pool: &mut RelayPool,
- action: RelayAction,
- ) {
+ pub(crate) fn process_relay_action(&mut self, remote: &mut RemoteApi<'_>, action: RelayAction) {
let acc = self.cache.selected_mut();
- modify_advertised_relays(&acc.key, action, pool, &self.relay_defaults, &mut acc.data);
-
- update_relay_configuration(
- pool,
+ modify_advertised_relays(
+ &acc.key,
+ action,
+ remote,
&self.relay_defaults,
- &acc.key.pubkey,
- &acc.data.relay,
- create_wakeup(ctx),
+ &mut acc.data,
);
- }
- pub fn get_subs(&self) -> &AccountSubs {
- &self.subs
+ self.retarget_selected_account_read_relays(remote);
}
pub fn selected_account_read_relays(&self) -> HashSet<NormRelayUrl> {
- self.get_selected_account()
- .data
- .relay
- .advertised
- .iter()
- .filter(|r| r.is_readable())
- .filter_map(|r| NormRelayUrl::new(&r.url).ok())
- .collect()
+ calculate_relays(
+ &self.relay_defaults,
+ &self.get_selected_account_data().relay,
+ true,
+ )
}
/// Return the selected account's advertised NIP-65 relays with marker metadata.
@@ -389,14 +348,23 @@ impl Accounts {
}
pub fn selected_account_write_relays(&self) -> Vec<RelayId> {
- self.get_selected_account()
- .data
- .relay
- .advertised
- .iter()
- .filter(|r| r.is_writable())
- .filter_map(|r| Some(RelayId::Websocket(NormRelayUrl::new(&r.url).ok()?)))
- .collect()
+ write_relays(
+ &self.relay_defaults,
+ &self.get_selected_account_data().relay,
+ )
+ }
+
+ fn ensure_selected_account_remote_subs(&mut self, remote: &mut RemoteApi<'_>) {
+ {
+ let mut scoped_subs = remote.scoped_subs(&*self);
+ ensure_selected_account_remote_subs_api(&mut scoped_subs, self);
+ }
+ self.scoped_remote_initialized = true;
+ }
+
+ fn retarget_selected_account_read_relays(&mut self, remote: &mut RemoteApi<'_>) {
+ remote.retarget_selected_account_read_relays(self);
+ self.scoped_remote_initialized = true;
}
}
@@ -414,13 +382,6 @@ impl<'a> AccType<'a> {
}
}
-fn create_wakeup(ctx: &egui::Context) -> impl Fn() + Send + Sync + Clone + 'static {
- let ctx = ctx.clone();
- move || {
- ctx.request_repaint();
- }
-}
-
fn add_account_from_storage(
cache: &mut AccountCache,
user_account_serializable: UserAccountSerializable,
@@ -473,22 +434,16 @@ impl AccountData {
}
}
- pub(super) fn poll_for_updates(
- &mut self,
- ndb: &Ndb,
- subs: &AccountSubs,
- ) -> Option<AccountDataUpdate> {
+ #[profiling::function]
+ pub(super) fn poll_for_updates(&mut self, ndb: &Ndb, ndb_subs: &AccountNdbSubs) -> bool {
let txn = Transaction::new(ndb).expect("txn");
- let mut resp = None;
- if self.relay.poll_for_updates(ndb, &txn, subs.relay.local) {
- resp = Some(AccountDataUpdate::Relay);
- }
+ let relay_updated = self.relay.poll_for_updates(ndb, &txn, ndb_subs.relay_ndb);
- self.muted.poll_for_updates(ndb, &txn, subs.mute.local);
+ self.muted.poll_for_updates(ndb, &txn, ndb_subs.mute_ndb);
self.contacts
- .poll_for_updates(ndb, &txn, subs.contacts.local);
+ .poll_for_updates(ndb, &txn, ndb_subs.contacts_ndb);
- resp
+ relay_updated
}
/// Note: query should be called as close to the subscription as possible
@@ -499,90 +454,125 @@ impl AccountData {
}
}
-pub(super) enum AccountDataUpdate {
- Relay,
-}
-
pub struct AddAccountResponse {
pub switch_to: Pubkey,
pub unk_id_action: SingleUnkIdAction,
}
-pub struct AccountSubs {
- relay: UnifiedSubscription,
- giftwraps: UnifiedSubscription,
- mute: UnifiedSubscription,
- pub contacts: UnifiedSubscription,
+fn giftwrap_filter(pk: &Pubkey) -> Filter {
+ // TODO: since optimize
+ nostrdb::Filter::new()
+ .kinds([1059])
+ .pubkeys([pk.bytes()])
+ .build()
}
-impl AccountSubs {
- pub(super) fn new(
- ndb: &mut Ndb,
- pool: &mut RelayPool,
- relay_defaults: &RelayDefaults,
- pk: &Pubkey,
- data: &AccountData,
- wakeup: impl Fn() + Send + Sync + Clone + 'static,
- ) -> Self {
- // TODO: since optimize
- let giftwraps_filter = nostrdb::Filter::new()
- .kinds([1059])
- .pubkeys([pk.bytes()])
- .build();
-
- update_relay_configuration(pool, relay_defaults, pk, &data.relay, wakeup);
+fn account_remote_owner_key() -> SubOwnerKey {
+ SubOwnerKey::new("core/accounts/remote-subs")
+}
- let relay = subscribe(ndb, pool, &data.relay.filter);
- let giftwraps = subscribe(ndb, pool, &giftwraps_filter);
- let mute = subscribe(ndb, pool, &data.muted.filter);
- let contacts = subscribe(ndb, pool, &data.contacts.filter);
+fn ensure_selected_account_remote_subs_api(
+ scoped_subs: &mut ScopedSubApi<'_, '_>,
+ accounts: &Accounts,
+) {
+ let owner = account_remote_owner_key();
+ for kind in account_remote_sub_kinds() {
+ let key = account_remote_sub_key(kind);
+ let identity = ScopedSubIdentity::account(owner, key);
+ let config = selected_account_remote_config(accounts, kind);
+ let _ = scoped_subs.ensure_sub(identity, config);
+ }
+}
- Self {
- relay,
- mute,
- contacts,
- giftwraps,
- }
+fn clear_account_remote_subs_for_account(
+ scoped_subs: &mut ScopedSubApi<'_, '_>,
+ account_pk: Pubkey,
+) {
+ let owner = account_remote_owner_key();
+ for kind in account_remote_sub_kinds() {
+ let key = account_remote_sub_key(kind);
+ let identity = ScopedSubIdentity::account(owner, key);
+ let _ = scoped_subs.clear_sub_for_account(account_pk, identity);
}
+}
- pub(super) fn swap_to(
- &mut self,
- ndb: &mut Ndb,
- pool: &mut RelayPool,
- relay_defaults: &RelayDefaults,
- pk: &Pubkey,
- new_selection_data: &AccountData,
- wakeup: impl Fn() + Send + Sync + Clone + 'static,
- ) {
- unsubscribe(ndb, pool, &self.relay);
- unsubscribe(ndb, pool, &self.mute);
- unsubscribe(ndb, pool, &self.contacts);
- unsubscribe(ndb, pool, &self.giftwraps);
+#[derive(Clone, Copy, Eq, Hash, PartialEq)]
+enum AccountRemoteSubKind {
+ RelayList,
+ MuteList,
+ ContactsList,
+ Giftwrap,
+}
- *self = AccountSubs::new(ndb, pool, relay_defaults, pk, new_selection_data, wakeup);
- }
+fn account_remote_sub_kinds() -> [AccountRemoteSubKind; 4] {
+ [
+ AccountRemoteSubKind::RelayList,
+ AccountRemoteSubKind::MuteList,
+ AccountRemoteSubKind::ContactsList,
+ AccountRemoteSubKind::Giftwrap,
+ ]
}
-fn subscribe(ndb: &Ndb, pool: &mut RelayPool, filter: &nostrdb::Filter) -> UnifiedSubscription {
- let filters = vec![filter.clone()];
- let sub = ndb
- .subscribe(&filters)
- .expect("ndb relay list subscription");
+fn account_remote_sub_key(kind: AccountRemoteSubKind) -> SubKey {
+ SubKey::new(kind)
+}
- // remote subscription
- let subid = Uuid::new_v4().to_string();
- pool.subscribe(subid.clone(), filters);
+fn make_account_remote_config(filters: Vec<Filter>, use_transparent: bool) -> SubConfig {
+ SubConfig {
+ relays: RelaySelection::AccountsRead,
+ filters,
+ use_transparent,
+ }
+}
- UnifiedSubscription {
- local: sub,
- remote: subid,
+fn selected_account_remote_config(accounts: &Accounts, kind: AccountRemoteSubKind) -> SubConfig {
+ let selected = accounts.get_selected_account_data();
+ match kind {
+ AccountRemoteSubKind::RelayList => {
+ make_account_remote_config(vec![selected.relay.filter.clone()], false)
+ }
+ AccountRemoteSubKind::MuteList => {
+ make_account_remote_config(vec![selected.muted.filter.clone()], false)
+ }
+ AccountRemoteSubKind::ContactsList => {
+ make_account_remote_config(vec![selected.contacts.filter.clone()], true)
+ }
+ AccountRemoteSubKind::Giftwrap => make_account_remote_config(
+ vec![giftwrap_filter(accounts.selected_account_pubkey())],
+ false,
+ ),
}
}
-fn unsubscribe(ndb: &mut Ndb, pool: &mut RelayPool, sub: &UnifiedSubscription) {
- pool.unsubscribe(sub.remote.clone());
+struct AccountNdbSubs {
+ relay_ndb: Subscription,
+ mute_ndb: Subscription,
+ contacts_ndb: Subscription,
+}
+
+impl AccountNdbSubs {
+ pub fn new(ndb: &mut Ndb, data: &AccountData) -> Self {
+ let relay_ndb = ndb
+ .subscribe(from_ref(&data.relay.filter))
+ .expect("ndb relay list subscription");
+ let mute_ndb = ndb
+ .subscribe(from_ref(&data.muted.filter))
+ .expect("ndb sub");
+ let contacts_ndb = ndb
+ .subscribe(from_ref(&data.contacts.filter))
+ .expect("ndb sub");
+ Self {
+ relay_ndb,
+ mute_ndb,
+ contacts_ndb,
+ }
+ }
- // local subscription
- ndb.unsubscribe(sub.local)
- .expect("ndb relay list unsubscribe");
+ pub fn swap_to(&mut self, ndb: &mut Ndb, new_selection_data: &AccountData) {
+ let _ = ndb.unsubscribe(self.relay_ndb);
+ let _ = ndb.unsubscribe(self.mute_ndb);
+ let _ = ndb.unsubscribe(self.contacts_ndb);
+
+ *self = AccountNdbSubs::new(ndb, new_selection_data);
+ }
}
diff --git a/crates/notedeck/src/account/relay.rs b/crates/notedeck/src/account/relay.rs
@@ -1,10 +1,10 @@
use std::collections::BTreeSet;
-use crate::{AccountData, RelaySpec};
-use enostr::{Keypair, Pubkey, RelayPool};
-use nostrdb::{Filter, Ndb, NoteBuilder, NoteKey, Subscription, Transaction};
+use crate::{AccountData, RelaySpec, RemoteApi};
+use enostr::{Keypair, NormRelayUrl, RelayId};
+use hashbrown::HashSet;
+use nostrdb::{Filter, Ndb, Note, NoteBuilder, NoteKey, Subscription, Transaction};
use tracing::{debug, error, info};
-use url::Url;
#[derive(Clone)]
pub(crate) struct AccountRelayData {
@@ -47,14 +47,6 @@ impl AccountRelayData {
self.advertised = relays.into_iter().collect()
}
- // standardize the format (ie, trailing slashes) to avoid dups
- pub fn canonicalize_url(url: &str) -> String {
- match Url::parse(url) {
- Ok(parsed_url) => parsed_url.to_string(),
- Err(_) => url.to_owned(), // If parsing fails, return the original URL.
- }
- }
-
pub(crate) fn harvest_nip65_relays(
ndb: &Ndb,
txn: &Transaction,
@@ -73,8 +65,12 @@ impl AccountRelayData {
let has_write_marker = tag
.get(2)
.is_some_and(|m| m.variant().str() == Some("write"));
+
+ let Ok(norm_url) = NormRelayUrl::new(url) else {
+ continue;
+ };
relays.push(RelaySpec::new(
- Self::canonicalize_url(url),
+ norm_url,
has_read_marker,
has_write_marker,
));
@@ -96,20 +92,23 @@ impl AccountRelayData {
relays
}
- pub fn publish_nip65_relays(&self, seckey: &[u8; 32], pool: &mut RelayPool) {
+ pub fn new_nip65_relays_note(&'_ self, seckey: &[u8; 32]) -> Note<'_> {
let mut builder = NoteBuilder::new().kind(10002).content("");
for rs in &self.advertised {
- builder = builder.start_tag().tag_str("r").tag_str(&rs.url);
+ builder = builder
+ .start_tag()
+ .tag_str("r")
+ .tag_str(&rs.url.to_string());
if rs.has_read_marker {
builder = builder.tag_str("read");
} else if rs.has_write_marker {
builder = builder.tag_str("write");
}
}
- let note = builder.sign(seckey).build().expect("note build");
- pool.send(&enostr::ClientMessage::event(¬e).expect("note client message"));
+ builder.sign(seckey).build().expect("note build")
}
+ #[profiling::function]
pub fn poll_for_updates(&mut self, ndb: &Ndb, txn: &Transaction, sub: Subscription) -> bool {
let nks = ndb.poll_for_notes(sub, 1);
@@ -134,7 +133,7 @@ impl RelayDefaults {
pub(crate) fn new(forced_relays: Vec<String>) -> Self {
let forced_relays: BTreeSet<RelaySpec> = forced_relays
.into_iter()
- .map(|u| RelaySpec::new(AccountRelayData::canonicalize_url(&u), false, false))
+ .filter_map(|u| Some(RelaySpec::new(NormRelayUrl::new(&u).ok()?, false, false)))
.collect();
let bootstrap_relays = [
"wss://relay.damus.io",
@@ -145,7 +144,7 @@ impl RelayDefaults {
]
.iter()
.map(|&url| url.to_string())
- .map(|u| RelaySpec::new(AccountRelayData::canonicalize_url(&u), false, false))
+ .filter_map(|u| Some(RelaySpec::new(NormRelayUrl::new(&u).ok()?, false, false)))
.collect();
Self {
@@ -155,25 +154,40 @@ impl RelayDefaults {
}
}
-pub(super) fn update_relay_configuration(
- pool: &mut RelayPool,
+pub fn calculate_relays(
relay_defaults: &RelayDefaults,
- pk: &Pubkey,
data: &AccountRelayData,
- wakeup: impl Fn() + Send + Sync + Clone + 'static,
-) {
- debug!(
- "updating relay configuration for currently selected {:?}",
- pk.hex()
- );
-
+ readable: bool, // are we calculating the readable relays? or the writable?
+) -> HashSet<NormRelayUrl> {
// If forced relays are set use them only
let mut desired_relays = relay_defaults.forced_relays.clone();
// Compose the desired relay lists from the selected account
if desired_relays.is_empty() {
- desired_relays.extend(data.local.iter().cloned());
- desired_relays.extend(data.advertised.iter().cloned());
+ desired_relays.extend(
+ data.local
+ .iter()
+ .filter(|l| {
+ if readable {
+ l.is_readable()
+ } else {
+ l.is_writable()
+ }
+ })
+ .cloned(),
+ );
+ desired_relays.extend(
+ data.advertised
+ .iter()
+ .filter(|l| {
+ if readable {
+ l.is_readable()
+ } else {
+ l.is_writable()
+ }
+ })
+ .cloned(),
+ );
}
// If no relays are specified at this point use the bootstrap list
@@ -181,33 +195,12 @@ pub(super) fn update_relay_configuration(
desired_relays = relay_defaults.bootstrap_relays.clone();
}
- debug!("current relays: {:?}", pool.urls());
debug!("desired relays: {:?}", desired_relays);
- let pool_specs = pool
- .urls()
- .iter()
- .map(|url| RelaySpec::new(url.clone(), false, false))
- .collect();
- let add: BTreeSet<RelaySpec> = desired_relays.difference(&pool_specs).cloned().collect();
- let mut sub: BTreeSet<RelaySpec> = pool_specs.difference(&desired_relays).cloned().collect();
- if !add.is_empty() {
- debug!("configuring added relays: {:?}", add);
- let _ = pool.add_urls(add.iter().map(|r| r.url.clone()).collect(), wakeup);
- }
- if !sub.is_empty() {
- // certain relays are persistent like the multicast relay,
- // although we should probably have a way to explicitly
- // disable it
- sub.remove(&RelaySpec::new("multicast", false, false));
-
- debug!("removing unwanted relays: {:?}", sub);
- pool.remove_urls(&sub.iter().map(|r| r.url.clone()).collect());
- }
-
- debug!("current relays: {:?}", pool.urls());
+ desired_relays.into_iter().map(|r| r.url).collect()
}
+// TODO(kernelkind): these should have `NormRelayUrl` instead of `String`...
pub enum RelayAction {
Add(String),
Remove(String),
@@ -225,14 +218,18 @@ impl RelayAction {
pub(super) fn modify_advertised_relays(
kp: &Keypair,
action: RelayAction,
- pool: &mut RelayPool,
+ remote: &mut RemoteApi<'_>,
relay_defaults: &RelayDefaults,
account_data: &mut AccountData,
) {
- let relay_url = AccountRelayData::canonicalize_url(action.get_url());
+ let Ok(relay_url) = NormRelayUrl::new(action.get_url()) else {
+ return;
+ };
+
+ let relay_url_str = relay_url.to_string();
match action {
- RelayAction::Add(_) => info!("add advertised relay \"{}\"", relay_url),
- RelayAction::Remove(_) => info!("remove advertised relay \"{}\"", relay_url),
+ RelayAction::Add(_) => info!("add advertised relay \"{relay_url_str}\""),
+ RelayAction::Remove(_) => info!("remove advertised relay \"{relay_url_str}\""),
}
// let selected = self.cache.selected_mut();
@@ -254,8 +251,22 @@ pub(super) fn modify_advertised_relays(
// If we have the secret key publish the NIP-65 relay list
if let Some(secretkey) = &kp.secret_key {
- account_data
+ let note = account_data
.relay
- .publish_nip65_relays(&secretkey.to_secret_bytes(), pool);
+ .new_nip65_relays_note(&secretkey.to_secret_bytes());
+
+ let mut publisher = remote.publisher_explicit();
+ publisher.publish_note(¬e, write_relays(relay_defaults, &account_data.relay));
}
}
+
+pub fn write_relays(relay_defaults: &RelayDefaults, data: &AccountRelayData) -> Vec<RelayId> {
+ let mut relays: Vec<RelayId> = calculate_relays(relay_defaults, data, false)
+ .into_iter()
+ .map(RelayId::Websocket)
+ .collect();
+
+ relays.push(RelayId::Multicast);
+
+ relays
+}
diff --git a/crates/notedeck/src/app.rs b/crates/notedeck/src/app.rs
@@ -146,9 +146,7 @@ impl eframe::App for Notedeck {
let mut app_ctx = self.app_context(ctx);
// handle account updates
- app_ctx
- .accounts
- .update(app_ctx.ndb, app_ctx.legacy_pool, ctx);
+ app_ctx.accounts.update(app_ctx.ndb, &mut app_ctx.remote);
app_ctx
.zaps
@@ -287,8 +285,6 @@ impl Notedeck {
FALLBACK_PUBKEY(),
&mut ndb,
&txn,
- &mut legacy_pool,
- ctx,
&mut unknown_ids,
);
@@ -307,11 +303,9 @@ impl Notedeck {
}
}
- if let Some(first) = parsed_args.keys.first() {
- accounts.select_account(&first.pubkey, &mut ndb, &txn, &mut legacy_pool, ctx);
- }
let outbox_session = if let Some(first) = parsed_args.keys.first() {
- let remote = RemoteApi::new(outbox_session, &mut scoped_sub_state);
+ let mut remote = RemoteApi::new(outbox_session, &mut scoped_sub_state);
+ accounts.select_account(&first.pubkey, &mut ndb, &txn, &mut remote);
remote.export_session()
} else {
outbox_session.export()
diff --git a/crates/notedeck/src/context.rs b/crates/notedeck/src/context.rs
@@ -5,8 +5,8 @@ use crate::{
};
use egui_winit::clipboard::Clipboard;
-use enostr::RelayPool;
-use nostrdb::Ndb;
+use enostr::{Pubkey, RelayPool};
+use nostrdb::{Ndb, Transaction};
#[cfg(target_os = "android")]
use android_activity::AndroidApp;
@@ -53,6 +53,21 @@ impl SoftKeyboardContext {
}
impl<'a> AppContext<'a> {
+ pub fn select_account(&mut self, pubkey: &Pubkey) {
+ let txn = Transaction::new(self.ndb).expect("txn");
+ self.accounts
+ .select_account(pubkey, self.ndb, &txn, &mut self.remote);
+ }
+
+ pub fn remove_account(&mut self, pubkey: &Pubkey) -> bool {
+ self.accounts
+ .remove_account(pubkey, self.ndb, &mut self.remote)
+ }
+
+ pub fn process_relay_action(&mut self, action: crate::RelayAction) {
+ self.accounts.process_relay_action(&mut self.remote, action);
+ }
+
pub fn soft_keyboard_rect(&self, screen_rect: Rect, ctx: SoftKeyboardContext) -> Option<Rect> {
match ctx {
SoftKeyboardContext::Virtual => {
diff --git a/crates/notedeck/src/lib.rs b/crates/notedeck/src/lib.rs
@@ -53,7 +53,7 @@ mod user_account;
mod wallet;
mod zaps;
-pub use account::accounts::{AccountData, AccountSubs, Accounts};
+pub use account::accounts::{AccountData, Accounts};
pub use account::contacts::{ContactState, IsFollowing};
pub use account::relay::RelayAction;
pub use account::FALLBACK_PUBKEY;
diff --git a/crates/notedeck/src/relayspec.rs b/crates/notedeck/src/relayspec.rs
@@ -1,30 +1,28 @@
use std::cmp::Ordering;
use std::fmt;
+use enostr::NormRelayUrl;
+
// A Relay specification includes NIP-65 defined "markers" which
// indicate if the relay should be used for reading or writing (or
// both).
#[derive(Clone)]
pub struct RelaySpec {
- pub url: String,
+ pub url: NormRelayUrl,
pub has_read_marker: bool,
pub has_write_marker: bool,
}
impl RelaySpec {
- pub fn new(
- url: impl Into<String>,
- mut has_read_marker: bool,
- mut has_write_marker: bool,
- ) -> Self {
+ pub fn new(url: NormRelayUrl, mut has_read_marker: bool, mut has_write_marker: bool) -> Self {
// if both markers are set turn both off ...
if has_read_marker && has_write_marker {
has_read_marker = false;
has_write_marker = false;
}
RelaySpec {
- url: url.into(),
+ url,
has_read_marker,
has_write_marker,
}
@@ -80,12 +78,12 @@ impl Eq for RelaySpec {}
#[allow(clippy::non_canonical_partial_ord_impl)]
impl PartialOrd for RelaySpec {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.url.cmp(&other.url))
+ Some(self.url.to_string().cmp(&other.url.to_string()))
}
}
impl Ord for RelaySpec {
fn cmp(&self, other: &Self) -> Ordering {
- self.url.cmp(&other.url)
+ self.url.to_string().cmp(&other.url.to_string())
}
}
diff --git a/crates/notedeck_columns/src/app.rs b/crates/notedeck_columns/src/app.rs
@@ -751,14 +751,7 @@ fn render_damus_mobile(
let kp = enostr::Keypair::only_pubkey(pubkey);
let _ = app_ctx.accounts.add_account(kp);
- let txn = nostrdb::Transaction::new(app_ctx.ndb).expect("txn");
- app_ctx.accounts.select_account(
- &pubkey,
- app_ctx.ndb,
- &txn,
- app_ctx.legacy_pool,
- ui.ctx(),
- );
+ app_ctx.select_account(&pubkey);
setup_selected_account_timeline_subs(
&mut app.timeline_cache,
app_ctx,
@@ -1011,14 +1004,7 @@ fn timelines_view(
// StripBuilder rendering
let mut save_cols = false;
if let Some(action) = side_panel_action {
- save_cols = save_cols
- || action.process(
- &mut app.timeline_cache,
- &mut app.decks_cache,
- ctx,
- &mut app.subscriptions,
- ui.ctx(),
- );
+ save_cols = save_cols || action.process(&mut app.timeline_cache, &mut app.decks_cache, ctx);
}
let mut app_action: Option<AppAction> = None;
@@ -1039,9 +1025,7 @@ fn timelines_view(
let kp = enostr::Keypair::only_pubkey(pubkey);
let _ = ctx.accounts.add_account(kp);
- let txn = nostrdb::Transaction::new(ctx.ndb).expect("txn");
- ctx.accounts
- .select_account(&pubkey, ctx.ndb, &txn, ctx.legacy_pool, ui.ctx());
+ ctx.select_account(&pubkey);
setup_selected_account_timeline_subs(&mut app.timeline_cache, ctx);
}
diff --git a/crates/notedeck_columns/src/nav.rs b/crates/notedeck_columns/src/nav.rs
@@ -8,11 +8,9 @@ use crate::{
profile::{ProfileAction, SaveProfileChanges},
repost::RepostAction,
route::{cleanup_popped_route, ColumnsRouter, Route, SingletonRouter},
- subscriptions::Subscriptions,
timeline::{
- kind::ListKind,
route::{render_thread_route, render_timeline_route},
- TimelineCache, TimelineKind,
+ TimelineCache,
},
ui::{
self,
@@ -90,31 +88,11 @@ impl SwitchingAction {
timeline_cache: &mut TimelineCache,
decks_cache: &mut DecksCache,
ctx: &mut AppContext<'_>,
- subs: &mut Subscriptions,
- ui_ctx: &egui::Context,
) -> bool {
match &self {
SwitchingAction::Accounts(account_action) => match account_action {
AccountsAction::Switch(switch_action) => {
- {
- let txn = Transaction::new(ctx.ndb).expect("txn");
- ctx.accounts.select_account(
- &switch_action.switch_to,
- ctx.ndb,
- &txn,
- ctx.legacy_pool,
- ui_ctx,
- );
-
- let contacts_sub = ctx.accounts.get_subs().contacts.remote.clone();
- // this is cringe but we're gonna get a new sub manager soon...
- subs.subs.insert(
- contacts_sub,
- crate::subscriptions::SubKind::FetchingContactList(TimelineKind::List(
- ListKind::Contact(*ctx.accounts.selected_account_pubkey()),
- )),
- );
- }
+ ctx.select_account(&switch_action.switch_to);
if switch_action.switching_to_new {
decks_cache.add_deck_default(ctx, timeline_cache, switch_action.switch_to);
@@ -129,10 +107,7 @@ impl SwitchingAction {
.go_back();
}
AccountsAction::Remove(to_remove) => 's: {
- if !ctx
- .accounts
- .remove_account(to_remove, ctx.ndb, ctx.legacy_pool, ui_ctx)
- {
+ if !ctx.remove_account(to_remove) {
break 's;
}
@@ -607,13 +582,7 @@ fn process_render_nav_action(
)
}
RenderNavAction::SwitchingAction(switching_action) => {
- if switching_action.process(
- &mut app.timeline_cache,
- &mut app.decks_cache,
- ctx,
- &mut app.subscriptions,
- ui.ctx(),
- ) {
+ if switching_action.process(&mut app.timeline_cache, &mut app.decks_cache, ctx) {
return Some(ProcessNavResult::SwitchOccurred);
} else {
return None;
@@ -632,8 +601,7 @@ fn process_render_nav_action(
wallet_action.process(ctx.accounts, ctx.global_wallet)
}
RenderNavAction::RelayAction(action) => {
- ctx.accounts
- .process_relay_action(ui.ctx(), ctx.legacy_pool, action);
+ ctx.process_relay_action(action);
None
}
RenderNavAction::SettingsAction(action) => {