mirror of
https://github.com/aljazceru/notedeck.git
synced 2025-12-18 17:14:21 +01:00
accounts: move relay stuff to own file
Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
use crate::account::relay::AccountRelayData;
|
||||
use crate::{
|
||||
AccountStorage, MuteFun, Muted, RelaySpec, SingleUnkIdAction, UnknownIds, UserAccount,
|
||||
};
|
||||
use enostr::{ClientMessage, FilledKeypair, Keypair, Pubkey, RelayPool};
|
||||
use nostrdb::{Filter, Ndb, Note, NoteBuilder, NoteKey, Subscription, Transaction};
|
||||
use nostrdb::{Filter, Ndb, Note, NoteKey, Subscription, Transaction};
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
// TODO: remove this
|
||||
@@ -34,14 +34,6 @@ pub enum AccountsAction {
|
||||
Remove(usize),
|
||||
}
|
||||
|
||||
pub struct AccountRelayData {
|
||||
filter: Filter,
|
||||
subid: Option<String>,
|
||||
sub: Option<Subscription>,
|
||||
local: BTreeSet<RelaySpec>, // used locally but not advertised
|
||||
advertised: BTreeSet<RelaySpec>, // advertised via NIP-65
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ContainsAccount {
|
||||
pub has_nsec: bool,
|
||||
@@ -54,136 +46,6 @@ pub struct AddAccountAction {
|
||||
pub unk_id_action: SingleUnkIdAction,
|
||||
}
|
||||
|
||||
impl AccountRelayData {
|
||||
pub fn new(ndb: &Ndb, pubkey: &[u8; 32]) -> Self {
|
||||
// Construct a filter for the user's NIP-65 relay list
|
||||
let filter = Filter::new()
|
||||
.authors([pubkey])
|
||||
.kinds([10002])
|
||||
.limit(1)
|
||||
.build();
|
||||
|
||||
// Query the ndb immediately to see if the user list is already there
|
||||
let txn = Transaction::new(ndb).expect("transaction");
|
||||
let lim = filter.limit().unwrap_or(crate::filter::default_limit()) as i32;
|
||||
let nks = ndb
|
||||
.query(&txn, &[filter.clone()], lim)
|
||||
.expect("query user relays results")
|
||||
.iter()
|
||||
.map(|qr| qr.note_key)
|
||||
.collect::<Vec<NoteKey>>();
|
||||
let relays = Self::harvest_nip65_relays(ndb, &txn, &nks);
|
||||
debug!(
|
||||
"pubkey {}: initial relays {:?}",
|
||||
hex::encode(pubkey),
|
||||
relays
|
||||
);
|
||||
|
||||
AccountRelayData {
|
||||
filter,
|
||||
subid: None,
|
||||
sub: None,
|
||||
local: BTreeSet::new(),
|
||||
advertised: relays.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
// make this account the current selected account
|
||||
pub fn activate(&mut self, ndb: &Ndb, pool: &mut RelayPool) {
|
||||
debug!("activating relay sub {}", self.filter.json().unwrap());
|
||||
assert_eq!(self.subid, None, "subid already exists");
|
||||
assert_eq!(self.sub, None, "sub already exists");
|
||||
|
||||
// local subscription
|
||||
let sub = ndb
|
||||
.subscribe(&[self.filter.clone()])
|
||||
.expect("ndb relay list subscription");
|
||||
|
||||
// remote subscription
|
||||
let subid = Uuid::new_v4().to_string();
|
||||
pool.subscribe(subid.clone(), vec![self.filter.clone()]);
|
||||
|
||||
self.sub = Some(sub);
|
||||
self.subid = Some(subid);
|
||||
}
|
||||
|
||||
// this account is no longer the selected account
|
||||
pub fn deactivate(&mut self, ndb: &mut Ndb, pool: &mut RelayPool) {
|
||||
debug!("deactivating relay sub {}", self.filter.json().unwrap());
|
||||
assert_ne!(self.subid, None, "subid doesn't exist");
|
||||
assert_ne!(self.sub, None, "sub doesn't exist");
|
||||
|
||||
// remote subscription
|
||||
pool.unsubscribe(self.subid.as_ref().unwrap().clone());
|
||||
|
||||
// local subscription
|
||||
ndb.unsubscribe(self.sub.unwrap())
|
||||
.expect("ndb relay list unsubscribe");
|
||||
|
||||
self.sub = None;
|
||||
self.subid = None;
|
||||
}
|
||||
|
||||
// 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.
|
||||
}
|
||||
}
|
||||
|
||||
fn harvest_nip65_relays(ndb: &Ndb, txn: &Transaction, nks: &[NoteKey]) -> Vec<RelaySpec> {
|
||||
let mut relays = Vec::new();
|
||||
for nk in nks.iter() {
|
||||
if let Ok(note) = ndb.get_note_by_key(txn, *nk) {
|
||||
for tag in note.tags() {
|
||||
match tag.get(0).and_then(|t| t.variant().str()) {
|
||||
Some("r") => {
|
||||
if let Some(url) = tag.get(1).and_then(|f| f.variant().str()) {
|
||||
let has_read_marker = tag
|
||||
.get(2)
|
||||
.is_some_and(|m| m.variant().str() == Some("read"));
|
||||
let has_write_marker = tag
|
||||
.get(2)
|
||||
.is_some_and(|m| m.variant().str() == Some("write"));
|
||||
relays.push(RelaySpec::new(
|
||||
Self::canonicalize_url(url),
|
||||
has_read_marker,
|
||||
has_write_marker,
|
||||
));
|
||||
}
|
||||
}
|
||||
Some("alt") => {
|
||||
// ignore for now
|
||||
}
|
||||
Some(x) => {
|
||||
error!("harvest_nip65_relays: unexpected tag type: {}", x);
|
||||
}
|
||||
None => {
|
||||
error!("harvest_nip65_relays: invalid tag");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
relays
|
||||
}
|
||||
|
||||
pub fn publish_nip65_relays(&self, seckey: &[u8; 32], pool: &mut RelayPool) {
|
||||
let mut builder = NoteBuilder::new().kind(10002).content("");
|
||||
for rs in &self.advertised {
|
||||
builder = builder.start_tag().tag_str("r").tag_str(&rs.url);
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AccountMutedData {
|
||||
filter: Filter,
|
||||
subid: Option<String>,
|
||||
@@ -299,8 +161,8 @@ impl AccountMutedData {
|
||||
}
|
||||
|
||||
pub struct AccountData {
|
||||
relay: AccountRelayData,
|
||||
muted: AccountMutedData,
|
||||
pub(crate) relay: AccountRelayData,
|
||||
pub(crate) muted: AccountMutedData,
|
||||
}
|
||||
|
||||
/// The interface for managing the user's accounts.
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
pub mod accounts;
|
||||
pub mod relay;
|
||||
|
||||
151
crates/notedeck/src/account/relay.rs
Normal file
151
crates/notedeck/src/account/relay.rs
Normal file
@@ -0,0 +1,151 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use enostr::RelayPool;
|
||||
use nostrdb::{Filter, Ndb, NoteBuilder, NoteKey, Subscription, Transaction};
|
||||
use tracing::{debug, error};
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::RelaySpec;
|
||||
|
||||
pub(crate) struct AccountRelayData {
|
||||
pub filter: Filter,
|
||||
pub subid: Option<String>,
|
||||
pub sub: Option<Subscription>,
|
||||
pub local: BTreeSet<RelaySpec>, // used locally but not advertised
|
||||
pub advertised: BTreeSet<RelaySpec>, // advertised via NIP-65
|
||||
}
|
||||
|
||||
impl AccountRelayData {
|
||||
pub fn new(ndb: &Ndb, pubkey: &[u8; 32]) -> Self {
|
||||
// Construct a filter for the user's NIP-65 relay list
|
||||
let filter = Filter::new()
|
||||
.authors([pubkey])
|
||||
.kinds([10002])
|
||||
.limit(1)
|
||||
.build();
|
||||
|
||||
// Query the ndb immediately to see if the user list is already there
|
||||
let txn = Transaction::new(ndb).expect("transaction");
|
||||
let lim = filter.limit().unwrap_or(crate::filter::default_limit()) as i32;
|
||||
let nks = ndb
|
||||
.query(&txn, &[filter.clone()], lim)
|
||||
.expect("query user relays results")
|
||||
.iter()
|
||||
.map(|qr| qr.note_key)
|
||||
.collect::<Vec<NoteKey>>();
|
||||
let relays = Self::harvest_nip65_relays(ndb, &txn, &nks);
|
||||
debug!(
|
||||
"pubkey {}: initial relays {:?}",
|
||||
hex::encode(pubkey),
|
||||
relays
|
||||
);
|
||||
|
||||
AccountRelayData {
|
||||
filter,
|
||||
subid: None,
|
||||
sub: None,
|
||||
local: BTreeSet::new(),
|
||||
advertised: relays.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
// make this account the current selected account
|
||||
pub fn activate(&mut self, ndb: &Ndb, pool: &mut RelayPool) {
|
||||
debug!("activating relay sub {}", self.filter.json().unwrap());
|
||||
assert_eq!(self.subid, None, "subid already exists");
|
||||
assert_eq!(self.sub, None, "sub already exists");
|
||||
|
||||
// local subscription
|
||||
let sub = ndb
|
||||
.subscribe(&[self.filter.clone()])
|
||||
.expect("ndb relay list subscription");
|
||||
|
||||
// remote subscription
|
||||
let subid = Uuid::new_v4().to_string();
|
||||
pool.subscribe(subid.clone(), vec![self.filter.clone()]);
|
||||
|
||||
self.sub = Some(sub);
|
||||
self.subid = Some(subid);
|
||||
}
|
||||
|
||||
// this account is no longer the selected account
|
||||
pub fn deactivate(&mut self, ndb: &mut Ndb, pool: &mut RelayPool) {
|
||||
debug!("deactivating relay sub {}", self.filter.json().unwrap());
|
||||
assert_ne!(self.subid, None, "subid doesn't exist");
|
||||
assert_ne!(self.sub, None, "sub doesn't exist");
|
||||
|
||||
// remote subscription
|
||||
pool.unsubscribe(self.subid.as_ref().unwrap().clone());
|
||||
|
||||
// local subscription
|
||||
ndb.unsubscribe(self.sub.unwrap())
|
||||
.expect("ndb relay list unsubscribe");
|
||||
|
||||
self.sub = None;
|
||||
self.subid = None;
|
||||
}
|
||||
|
||||
// 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,
|
||||
nks: &[NoteKey],
|
||||
) -> Vec<RelaySpec> {
|
||||
let mut relays = Vec::new();
|
||||
for nk in nks.iter() {
|
||||
if let Ok(note) = ndb.get_note_by_key(txn, *nk) {
|
||||
for tag in note.tags() {
|
||||
match tag.get(0).and_then(|t| t.variant().str()) {
|
||||
Some("r") => {
|
||||
if let Some(url) = tag.get(1).and_then(|f| f.variant().str()) {
|
||||
let has_read_marker = tag
|
||||
.get(2)
|
||||
.is_some_and(|m| m.variant().str() == Some("read"));
|
||||
let has_write_marker = tag
|
||||
.get(2)
|
||||
.is_some_and(|m| m.variant().str() == Some("write"));
|
||||
relays.push(RelaySpec::new(
|
||||
Self::canonicalize_url(url),
|
||||
has_read_marker,
|
||||
has_write_marker,
|
||||
));
|
||||
}
|
||||
}
|
||||
Some("alt") => {
|
||||
// ignore for now
|
||||
}
|
||||
Some(x) => {
|
||||
error!("harvest_nip65_relays: unexpected tag type: {}", x);
|
||||
}
|
||||
None => {
|
||||
error!("harvest_nip65_relays: invalid tag");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
relays
|
||||
}
|
||||
|
||||
pub fn publish_nip65_relays(&self, seckey: &[u8; 32], pool: &mut RelayPool) {
|
||||
let mut builder = NoteBuilder::new().kind(10002).content("");
|
||||
for rs in &self.advertised {
|
||||
builder = builder.start_tag().tag_str("r").tag_str(&rs.url);
|
||||
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"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user