upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2024-07-18 12:14:09 +0100
committerDanConwayDev <DanConwayDev@protonmail.com>2024-07-18 12:31:08 +0100
commit6f66fab0b5bab74e63c790d8964a0e6b4982000e (patch)
tree4ed4675c78c192dfb13f544201b0cb7b37db0400 /src
parent10d98590ef9317be5b12a6a4830d7394d479c1d5 (diff)
refactor: fetch some profiles from user relays
add the ability to fetch more than just the current user from their user write relays
Diffstat (limited to 'src')
-rw-r--r--src/client.rs102
-rw-r--r--src/login.rs111
2 files changed, 123 insertions, 90 deletions
diff --git a/src/client.rs b/src/client.rs
index b50ccf2..1fdc3d8 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -35,7 +35,7 @@ use nostr_sqlite::SQLiteDatabase;
35 35
36use crate::{ 36use crate::{
37 config::get_dirs, 37 config::get_dirs,
38 login::get_logged_in_user_and_relays_from_cache, 38 login::{get_logged_in_user, get_user_ref_from_cache},
39 repo_ref::{RepoRef, REPO_REF_KIND}, 39 repo_ref::{RepoRef, REPO_REF_KIND},
40 sub_commands::{ 40 sub_commands::{
41 list::status_kinds, 41 list::status_kinds,
@@ -313,8 +313,8 @@ impl Connect for Client {
313 313
314 loop { 314 loop {
315 let relays = request 315 let relays = request
316 .relays 316 .repo_relays
317 .union(&request.current_user_write_relays) 317 .union(&request.user_relays_for_profiles)
318 // don't look for events on blaster 318 // don't look for events on blaster
319 .filter(|&r| !r.as_str().contains("nostr.mutinywallet.com")) 319 .filter(|&r| !r.as_str().contains("nostr.mutinywallet.com"))
320 .cloned() 320 .cloned()
@@ -325,11 +325,11 @@ impl Connect for Client {
325 if relays.is_empty() { 325 if relays.is_empty() {
326 break; 326 break;
327 } 327 }
328 let only_user_relays = request 328 let profile_relays_only = request
329 .current_user_write_relays 329 .user_relays_for_profiles
330 .difference(&request.relays) 330 .difference(&request.repo_relays)
331 .collect::<HashSet<&Url>>(); 331 .collect::<HashSet<&Url>>();
332 for relay in &request.relays { 332 for relay in &request.repo_relays {
333 self.client 333 self.client
334 .add_relay(relay.as_str()) 334 .add_relay(relay.as_str())
335 .await 335 .await
@@ -341,15 +341,17 @@ impl Connect for Client {
341 let futures: Vec<_> = relays 341 let futures: Vec<_> = relays
342 .iter() 342 .iter()
343 .map(|r| { 343 .map(|r| {
344 if only_user_relays.contains(r) { 344 if profile_relays_only.contains(r) {
345 // if user write relay isn't a repo relay, just filter for user profile 345 // if relay isn't a repo relay, just filter for user profile
346 FetchRequest { 346 FetchRequest {
347 selected_relay: Some(r.to_owned()), 347 selected_relay: Some(r.to_owned()),
348 repo_coordinates: vec![], 348 repo_coordinates: vec![],
349 proposals: HashSet::new(), 349 proposals: HashSet::new(),
350 missing_contributor_profiles: HashSet::from_iter(vec![ 350 missing_contributor_profiles: request
351 request.current_user.unwrap(), 351 .missing_contributor_profiles
352 ]), 352 .union(&request.profiles_to_fetch_from_user_relays)
353 .copied()
354 .collect(),
353 ..request.clone() 355 ..request.clone()
354 } 356 }
355 } else { 357 } else {
@@ -420,15 +422,26 @@ impl Connect for Client {
420 processed_relays.extend(relays.clone()); 422 processed_relays.extend(relays.clone());
421 423
422 if let Ok(repo_ref) = get_repo_ref_from_cache(git_repo_path, repo_coordinates).await { 424 if let Ok(repo_ref) = get_repo_ref_from_cache(git_repo_path, repo_coordinates).await {
423 request.relays = repo_ref 425 request.repo_relays = repo_ref
424 .relays 426 .relays
425 .iter() 427 .iter()
426 .filter_map(|r| Url::parse(r).ok()) 428 .filter_map(|r| Url::parse(r).ok())
427 .collect(); 429 .collect();
428 } 430 }
429 let (_, current_user_write_relays) = 431
430 get_logged_in_user_and_relays_from_cache(git_repo_path).await?; 432 request.user_relays_for_profiles = {
431 request.current_user_write_relays = current_user_write_relays; 433 let mut set = HashSet::new();
434 for user in &request.profiles_to_fetch_from_user_relays {
435 if let Ok(user_ref) = get_user_ref_from_cache(git_repo_path, user).await {
436 for r in user_ref.relays.write() {
437 if let Ok(url) = Url::parse(&r) {
438 set.insert(url);
439 }
440 }
441 }
442 }
443 set
444 };
432 } 445 }
433 Ok((relay_reports, progress_reporter)) 446 Ok((relay_reports, progress_reporter))
434 } 447 }
@@ -444,10 +457,11 @@ impl Connect for Client {
444 fresh_coordinates.insert(c); 457 fresh_coordinates.insert(c);
445 } 458 }
446 let mut fresh_proposal_roots = request.proposals.clone(); 459 let mut fresh_proposal_roots = request.proposals.clone();
447 let mut fresh_contributors = request.missing_contributor_profiles.clone(); 460 let mut fresh_contributors = request
448 if let Some(user) = request.current_user { 461 .missing_contributor_profiles
449 fresh_contributors.insert(user); 462 .union(&request.profiles_to_fetch_from_user_relays)
450 } 463 .copied()
464 .collect();
451 465
452 let mut report = FetchReport::default(); 466 let mut report = FetchReport::default();
453 467
@@ -870,19 +884,39 @@ async fn create_relays_request(
870 } 884 }
871 } 885 }
872 886
873 let (current_user, current_user_write_relays) = 887 let profiles_to_fetch_from_user_relays = {
874 get_logged_in_user_and_relays_from_cache(git_repo_path).await?; 888 let mut set = user_profiles.clone();
875 if let Some(current_user) = current_user { 889 if let Ok(Some(current_user)) = get_logged_in_user(git_repo_path).await {
876 missing_contributor_profiles.insert(current_user); 890 set.insert(current_user);
877 } 891 }
878 missing_contributor_profiles.extend(user_profiles); 892 set
893 };
894
895 let user_relays_for_profiles = {
896 let mut set = HashSet::new();
897 for user in &profiles_to_fetch_from_user_relays {
898 if let Ok(user_ref) = get_user_ref_from_cache(git_repo_path, user).await {
899 for r in user_ref.relays.write() {
900 if let Ok(url) = Url::parse(&r) {
901 set.insert(url);
902 }
903 }
904 } else {
905 missing_contributor_profiles.insert(user.to_owned());
906 }
907 }
908 set
909 };
879 910
880 let existing_events: HashSet<EventId> = { 911 let existing_events: HashSet<EventId> = {
881 let mut existing_events: HashSet<EventId> = HashSet::new(); 912 let mut existing_events: HashSet<EventId> = HashSet::new();
882 for filter in get_fetch_filters( 913 for filter in get_fetch_filters(
883 &repo_coordinates_without_relays, 914 &repo_coordinates_without_relays,
884 &proposals, 915 &proposals,
885 &missing_contributor_profiles, 916 &missing_contributor_profiles
917 .union(&profiles_to_fetch_from_user_relays)
918 .copied()
919 .collect(),
886 ) { 920 ) {
887 for (id, _) in get_local_cache_database(git_repo_path) 921 for (id, _) in get_local_cache_database(git_repo_path)
888 .await? 922 .await?
@@ -915,7 +949,7 @@ async fn create_relays_request(
915 }; 949 };
916 950
917 let relay_column_width = relays 951 let relay_column_width = relays
918 .union(&current_user_write_relays) 952 .union(&user_relays_for_profiles)
919 .reduce(|a, r| { 953 .reduce(|a, r| {
920 if r.to_string() 954 if r.to_string()
921 .chars() 955 .chars()
@@ -935,7 +969,7 @@ async fn create_relays_request(
935 969
936 Ok(FetchRequest { 970 Ok(FetchRequest {
937 selected_relay: None, 971 selected_relay: None,
938 relays, 972 repo_relays: relays,
939 relay_column_width, 973 relay_column_width,
940 repo_coordinates: if let Ok(repo_ref) = repo_ref { 974 repo_coordinates: if let Ok(repo_ref) = repo_ref {
941 repo_ref.coordinates_with_timestamps() 975 repo_ref.coordinates_with_timestamps()
@@ -949,8 +983,8 @@ async fn create_relays_request(
949 contributors, 983 contributors,
950 missing_contributor_profiles, 984 missing_contributor_profiles,
951 existing_events, 985 existing_events,
952 current_user, 986 profiles_to_fetch_from_user_relays,
953 current_user_write_relays, 987 user_relays_for_profiles,
954 }) 988 })
955} 989}
956 990
@@ -1226,7 +1260,7 @@ impl Display for FetchReport {
1226 1260
1227#[derive(Default, Clone)] 1261#[derive(Default, Clone)]
1228pub struct FetchRequest { 1262pub struct FetchRequest {
1229 relays: HashSet<Url>, 1263 repo_relays: HashSet<Url>,
1230 selected_relay: Option<Url>, 1264 selected_relay: Option<Url>,
1231 relay_column_width: usize, 1265 relay_column_width: usize,
1232 repo_coordinates: Vec<(Coordinate, Option<Timestamp>)>, 1266 repo_coordinates: Vec<(Coordinate, Option<Timestamp>)>,
@@ -1234,6 +1268,6 @@ pub struct FetchRequest {
1234 contributors: HashSet<PublicKey>, 1268 contributors: HashSet<PublicKey>,
1235 missing_contributor_profiles: HashSet<PublicKey>, 1269 missing_contributor_profiles: HashSet<PublicKey>,
1236 existing_events: HashSet<EventId>, 1270 existing_events: HashSet<EventId>,
1237 current_user: Option<PublicKey>, 1271 profiles_to_fetch_from_user_relays: HashSet<PublicKey>,
1238 current_user_write_relays: HashSet<Url>, 1272 user_relays_for_profiles: HashSet<Url>,
1239} 1273}
diff --git a/src/login.rs b/src/login.rs
index 6c3acfa..aac0141 100644
--- a/src/login.rs
+++ b/src/login.rs
@@ -6,7 +6,7 @@ use nostr::{
6 PublicKey, 6 PublicKey,
7}; 7};
8use nostr_sdk::{ 8use nostr_sdk::{
9 Alphabet, FromBech32, JsonUtil, Keys, Kind, NostrSigner, SingleLetterTag, ToBech32, Url, 9 Alphabet, FromBech32, JsonUtil, Keys, Kind, NostrSigner, SingleLetterTag, ToBech32,
10}; 10};
11use nostr_signer::Nip46Signer; 11use nostr_signer::Nip46Signer;
12 12
@@ -57,7 +57,8 @@ pub async fn launch(
57 get_config_item(git_repo, "nostr.npub") 57 get_config_item(git_repo, "nostr.npub")
58 .unwrap_or("unknown ncryptsec".to_string()), 58 .unwrap_or("unknown ncryptsec".to_string()),
59 ) { 59 ) {
60 if let Ok(user_ref) = get_user_details(&public_key, client, git_repo).await 60 if let Ok(user_ref) =
61 get_user_details(&public_key, client, git_repo.get_path()?).await
61 { 62 {
62 user_ref.metadata.name 63 user_ref.metadata.name
63 } else { 64 } else {
@@ -92,7 +93,7 @@ pub async fn launch(
92 .await 93 .await
93 .context("cannot get public key from signer")?, 94 .context("cannot get public key from signer")?,
94 client, 95 client,
95 git_repo, 96 git_repo.get_path()?,
96 ) 97 )
97 .await?; 98 .await?;
98 print_logged_in_as(&user_ref, client.is_none())?; 99 print_logged_in_as(&user_ref, client.is_none())?;
@@ -395,7 +396,7 @@ async fn fresh_login(
395 signer.public_key().await? 396 signer.public_key().await?
396 }; 397 };
397 // lookup profile 398 // lookup profile
398 let user_ref = get_user_details(&public_key, client, git_repo).await?; 399 let user_ref = get_user_details(&public_key, client, git_repo.get_path()?).await?;
399 print_logged_in_as(&user_ref, client.is_none())?; 400 print_logged_in_as(&user_ref, client.is_none())?;
400 Ok((signer, user_ref)) 401 Ok((signer, user_ref))
401} 402}
@@ -610,77 +611,75 @@ async fn get_user_details(
610 public_key: &PublicKey, 611 public_key: &PublicKey,
611 #[cfg(test)] client: Option<&crate::client::MockConnect>, 612 #[cfg(test)] client: Option<&crate::client::MockConnect>,
612 #[cfg(not(test))] client: Option<&Client>, 613 #[cfg(not(test))] client: Option<&Client>,
613 git_repo: &Repo, 614 git_repo_path: &Path,
614) -> Result<UserRef> { 615) -> Result<UserRef> {
615 let filters = vec![ 616 if let Ok(user_ref) = get_user_ref_from_cache(git_repo_path, public_key).await {
616 nostr::Filter::default() 617 Ok(user_ref)
617 .author(*public_key) 618 } else {
618 .kind(Kind::Metadata), 619 let empty = UserRef {
619 nostr::Filter::default() 620 public_key: public_key.to_owned(),
620 .author(*public_key) 621 metadata: extract_user_metadata(public_key, &[])?,
621 .kind(Kind::RelayList), 622 relays: extract_user_relays(public_key, &[]),
622 ]; 623 };
623
624 let mut events = get_event_from_global_cache(git_repo.get_path()?, filters.clone()).await?;
625 624
626 if let Some(client) = client { 625 if let Some(client) = client {
627 if events.is_empty() {
628 let term = console::Term::stderr(); 626 let term = console::Term::stderr();
629 term.write_line("searching for profile...")?; 627 term.write_line("searching for profile...")?;
630 let (_, progress_reporter) = client 628 let (_, progress_reporter) = client
631 .fetch_all( 629 .fetch_all(
632 git_repo.get_path()?, 630 git_repo_path,
633 &HashSet::new(), 631 &HashSet::new(),
634 &HashSet::from_iter(vec![*public_key]), 632 &HashSet::from_iter(vec![*public_key]),
635 ) 633 )
636 .await?; 634 .await?;
637 events = get_event_from_global_cache(git_repo.get_path()?, filters).await?; 635 if let Ok(user_ref) = get_user_ref_from_cache(git_repo_path, public_key).await {
638 if !events.is_empty() {
639 progress_reporter.clear()?; 636 progress_reporter.clear()?;
640 // term.clear_last_lines(1)?; 637 // term.clear_last_lines(1)?;
638 Ok(user_ref)
639 } else {
640 Ok(empty)
641 } 641 }
642 } else {
643 Ok(empty)
642 } 644 }
643 } 645 }
646}
647pub async fn get_logged_in_user(git_repo_path: &Path) -> Result<Option<PublicKey>> {
648 let git_repo = Repo::from_path(&git_repo_path.to_path_buf())?;
649 Ok(
650 if let Some(npub) = git_repo.get_git_config_item("nostr.npub", None)? {
651 if let Ok(pubic_key) = PublicKey::parse(npub) {
652 Some(pubic_key)
653 } else {
654 None
655 }
656 } else {
657 None
658 },
659 )
660}
661
662pub async fn get_user_ref_from_cache(
663 git_repo_path: &Path,
664 public_key: &PublicKey,
665) -> Result<UserRef> {
666 let filters = vec![
667 nostr::Filter::default()
668 .author(*public_key)
669 .kind(Kind::Metadata),
670 nostr::Filter::default()
671 .author(*public_key)
672 .kind(Kind::RelayList),
673 ];
674
675 let events = get_event_from_global_cache(git_repo_path, filters.clone()).await?;
644 676
677 if events.is_empty() {
678 bail!("no metadata and profile list in cache for selected public key");
679 }
645 Ok(UserRef { 680 Ok(UserRef {
646 public_key: public_key.to_owned(), 681 public_key: public_key.to_owned(),
647 metadata: extract_user_metadata(public_key, &events)?, 682 metadata: extract_user_metadata(public_key, &events)?,
648 relays: extract_user_relays(public_key, &events), 683 relays: extract_user_relays(public_key, &events),
649 }) 684 })
650} 685}
651
652pub async fn get_logged_in_user_and_relays_from_cache(
653 git_repo_path: &Path,
654) -> Result<(Option<PublicKey>, HashSet<Url>)> {
655 let git_repo = Repo::from_path(&git_repo_path.to_path_buf())?;
656 let current_user = if let Some(npub) = git_repo.get_git_config_item("nostr.npub", None)? {
657 if let Ok(pubic_key) = PublicKey::parse(npub) {
658 Some(pubic_key)
659 } else {
660 None
661 }
662 } else {
663 None
664 };
665 let relays = if let Some(current_user) = current_user {
666 extract_user_relays(
667 &current_user,
668 &get_event_from_global_cache(
669 git_repo.get_path()?,
670 vec![
671 nostr::Filter::default()
672 .author((*current_user).into())
673 .kind(Kind::RelayList),
674 ],
675 )
676 .await?,
677 )
678 .write()
679 .iter()
680 .filter_map(|r| Url::parse(r).ok())
681 .collect::<HashSet<Url>>()
682 } else {
683 HashSet::new()
684 };
685 Ok((current_user, relays))
686}