From 43a445963968dac7da190b56f7c89ac0ff1f6abd Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Wed, 17 Jul 2024 10:53:20 +0100 Subject: feat(login): use fetch to get user profile fetch automatically gets updates to logged in user profile / relays fetching without specifying repo pointers will just fetch user profiles so that can be used during login, if user profile isn't in cache login now uses fetch --- src/client.rs | 156 ++++++++++++++++++++++++++++------------------ src/git.rs | 8 +-- src/login.rs | 124 +++++++++++++++++------------------- src/sub_commands/fetch.rs | 11 +++- 4 files changed, 165 insertions(+), 134 deletions(-) (limited to 'src') diff --git a/src/client.rs b/src/client.rs index 835d69e..b6e93d6 100644 --- a/src/client.rs +++ b/src/client.rs @@ -35,6 +35,7 @@ use nostr_sqlite::SQLiteDatabase; use crate::{ config::get_dirs, + login::get_logged_in_user_and_relays_from_cache, repo_ref::{RepoRef, REPO_REF_KIND}, sub_commands::{ list::status_kinds, @@ -77,7 +78,7 @@ pub trait Connect { &self, git_repo_path: &Path, repo_coordinates: &HashSet, - ) -> Result; + ) -> Result<(Vec>, MultiProgress)>; async fn fetch_all_from_relay( &self, git_repo_path: &Path, @@ -287,8 +288,7 @@ impl Connect for Client { &self, git_repo_path: &Path, repo_coordinates: &HashSet, - ) -> Result { - println!("fetching updates..."); + ) -> Result<(Vec>, MultiProgress)> { let fallback_relays = &self .fallback_relays .iter() @@ -305,6 +305,23 @@ impl Connect for Client { let mut relay_reports: Vec> = vec![]; loop { + let relays = request + .relays + .union(&request.current_user_write_relays) + // don't look for events on blaster + .filter(|&r| !r.as_str().contains("nostr.mutinywallet.com")) + .cloned() + .collect::>() + .difference(&processed_relays) + .cloned() + .collect::>(); + if relays.is_empty() { + break; + } + let only_user_relays = request + .current_user_write_relays + .difference(&request.relays) + .collect::>(); for relay in &request.relays { self.client .add_relay(relay.as_str()) @@ -314,14 +331,26 @@ impl Connect for Client { let dim = Style::new().color256(247); - let futures: Vec<_> = request - .relays + let futures: Vec<_> = relays .iter() - // don't look for events on blaster - .filter(|r| !r.as_str().contains("nostr.mutinywallet.com")) - .map(|r| FetchRequest { - selected_relay: Some(r.clone()), - ..request.clone() + .map(|r| { + if only_user_relays.contains(r) { + // if user write relay isn't a repo relay, just filter for user profile + FetchRequest { + selected_relay: Some(r.to_owned()), + repo_coordinates: vec![], + proposals: HashSet::new(), + missing_contributor_profiles: HashSet::from_iter(vec![ + request.current_user.unwrap(), + ]), + ..request.clone() + } + } else { + FetchRequest { + selected_relay: Some(r.to_owned()), + ..request.clone() + } + } }) .map(|request| async { let relay_column_width = request.relay_column_width; @@ -381,31 +410,20 @@ impl Connect for Client { { relay_reports.push(report); } - - for relay in &request.relays { - processed_relays.insert(relay.clone()); - } + processed_relays.extend(relays.clone()); if let Ok(repo_ref) = get_repo_ref_from_cache(git_repo_path, repo_coordinates).await { request.relays = repo_ref .relays .iter() .filter_map(|r| Url::parse(r).ok()) - .filter(|r| !processed_relays.contains(r)) .collect(); - if request.relays.is_empty() { - break; - } } + let (_, current_user_write_relays) = + get_logged_in_user_and_relays_from_cache(git_repo_path).await?; + request.current_user_write_relays = current_user_write_relays; } - let report = consolidate_fetch_reports(relay_reports); - - if report.to_string().is_empty() { - println!("no updates"); - } else { - println!("updates: {report}"); - } - Ok(report) + Ok((relay_reports, progress_reporter)) } async fn fetch_all_from_relay( @@ -420,6 +438,9 @@ impl Connect for Client { } let mut fresh_proposal_roots = request.proposals.clone(); let mut fresh_contributors = request.missing_contributor_profiles.clone(); + if let Some(user) = request.current_user { + fresh_contributors.insert(user); + } let mut report = FetchReport::default(); @@ -486,9 +507,9 @@ impl Connect for Client { "{: Result { let repo_ref = get_repo_ref_from_cache(git_repo_path, repo_coordinates).await; - let relays = { - let mut relays = fallback_relays; - if let Ok(repo_ref) = &repo_ref { - for r in &repo_ref.relays { - if let Ok(url) = Url::parse(r) { - relays.insert(url); - } - } - } - relays - }; - - let relay_column_width = relays - .iter() - .reduce(|a, r| { - if r.to_string() - .chars() - .count() - .gt(&a.to_string().chars().count()) - { - r - } else { - a - } - }) - .unwrap() - .to_string() - .chars() - .count() - + 2; - let repo_coordinates = if let Ok(repo_ref) = &repo_ref { repo_ref.coordinates() } else { @@ -802,7 +792,7 @@ async fn create_relays_request( let mut missing_contributor_profiles: HashSet = HashSet::new(); let mut contributors: HashSet = HashSet::new(); - { + if !repo_coordinates.is_empty() { if let Ok(repo_ref) = &repo_ref { for m in &repo_ref.maintainers { contributors.insert(m.to_owned()); @@ -848,6 +838,12 @@ async fn create_relays_request( } } + let (current_user, current_user_write_relays) = + get_logged_in_user_and_relays_from_cache(git_repo_path).await?; + if let Some(current_user) = current_user { + missing_contributor_profiles.insert(current_user); + } + let existing_events: HashSet = { let mut existing_events: HashSet = HashSet::new(); for filter in @@ -863,6 +859,38 @@ async fn create_relays_request( } existing_events }; + + let relays = { + let mut relays = fallback_relays; + if let Ok(repo_ref) = &repo_ref { + for r in &repo_ref.relays { + if let Ok(url) = Url::parse(r) { + relays.insert(url); + } + } + } + relays + }; + + let relay_column_width = relays + .union(¤t_user_write_relays) + .reduce(|a, r| { + if r.to_string() + .chars() + .count() + .gt(&a.to_string().chars().count()) + { + r + } else { + a + } + }) + .unwrap() + .to_string() + .chars() + .count() + + 2; + Ok(FetchRequest { selected_relay: None, relays, @@ -876,6 +904,8 @@ async fn create_relays_request( contributors, missing_contributor_profiles, existing_events, + current_user, + current_user_write_relays, }) } @@ -972,7 +1002,7 @@ async fn process_fetched_events( Ok(()) } -fn consolidate_fetch_reports(reports: Vec>) -> FetchReport { +pub fn consolidate_fetch_reports(reports: Vec>) -> FetchReport { let mut report = FetchReport::default(); for relay_report in reports.into_iter().flatten() { for c in relay_report.repo_coordinates { @@ -1136,7 +1166,7 @@ impl Display for FetchReport { } if !self.contributor_profiles.is_empty() { display_items.push(format!( - "{} contributor profile{}", + "{} user profile{}", self.contributor_profiles.len(), if self.contributor_profiles.len() > 1 { "s" @@ -1159,4 +1189,6 @@ pub struct FetchRequest { contributors: HashSet, missing_contributor_profiles: HashSet, existing_events: HashSet, + current_user: Option, + current_user_write_relays: HashSet, } diff --git a/src/git.rs b/src/git.rs index bb943a9..c13b46d 100644 --- a/src/git.rs +++ b/src/git.rs @@ -1,6 +1,7 @@ -#[cfg(test)] -use std::path::PathBuf; -use std::{env::current_dir, path::Path}; +use std::{ + env::current_dir, + path::{Path, PathBuf}, +}; use anyhow::{bail, Context, Result}; use git2::{DiffOptions, Oid, Revwalk}; @@ -18,7 +19,6 @@ impl Repo { git_repo: git2::Repository::discover(current_dir()?)?, }) } - #[cfg(test)] pub fn from_path(path: &PathBuf) -> Result { Ok(Self { git_repo: git2::Repository::open(path)?, diff --git a/src/login.rs b/src/login.rs index d580969..0a20a90 100644 --- a/src/login.rs +++ b/src/login.rs @@ -1,17 +1,14 @@ -use std::{fs::create_dir_all, str::FromStr, time::Duration}; +use std::{collections::HashSet, path::Path, str::FromStr, time::Duration}; use anyhow::{bail, Context, Result}; use nostr::{ nips::{nip05::get_nip46, nip46::NostrConnectURI}, PublicKey, }; -use nostr_database::Order; use nostr_sdk::{ - Alphabet, FromBech32, JsonUtil, Keys, Kind, NostrDatabase, NostrSigner, SingleLetterTag, - ToBech32, + Alphabet, FromBech32, JsonUtil, Keys, Kind, NostrSigner, SingleLetterTag, ToBech32, Url, }; use nostr_signer::Nip46Signer; -use nostr_sqlite::SQLiteDatabase; #[cfg(not(test))] use crate::client::Client; @@ -21,8 +18,8 @@ use crate::{ cli_interactor::{ Interactor, InteractorPrompt, PromptConfirmParms, PromptInputParms, PromptPasswordParms, }, - client::{fetch_public_key, Connect}, - config::{get_dirs, UserMetadata, UserRef, UserRelayRef, UserRelays}, + client::{fetch_public_key, get_event_from_global_cache, Connect}, + config::{UserMetadata, UserRef, UserRelayRef, UserRelays}, git::{Repo, RepoActions}, key_handling::encryption::{decrypt_key, encrypt_key}, }; @@ -107,7 +104,7 @@ pub async fn launch( fn print_logged_in_as(user_ref: &UserRef, offline_mode: bool) -> Result<()> { if !offline_mode && user_ref.metadata.created_at.eq(&0) { - println!("cannot find your account metadata (name, etc) on relays"); + println!("cannot find profile..."); } else if !offline_mode && user_ref.metadata.name.eq(&user_ref.public_key.to_bech32()?) { println!("cannot extract account name from account metadata..."); } else if !offline_mode && user_ref.relays.created_at.eq(&0) { @@ -615,20 +612,6 @@ async fn get_user_details( #[cfg(not(test))] client: Option<&Client>, git_repo: &Repo, ) -> Result { - if client.is_some() { - println!("searching for profile and relay updates..."); - } - let database = SQLiteDatabase::open(if std::env::var("NGITTEST").is_err() { - create_dir_all(get_dirs()?.config_dir()).context(format!( - "cannot create cache directory in: {:?}", - get_dirs()?.config_dir() - ))?; - get_dirs()?.config_dir().join("cache.sqlite") - } else { - git_repo.get_path()?.join(".git/test-global-cache.sqlite") - }) - .await?; - let mut events: Vec = vec![]; let filters = vec![ nostr::Filter::default() .author(*public_key) @@ -637,54 +620,63 @@ async fn get_user_details( .author(*public_key) .kind(Kind::RelayList), ]; - if let Ok(cached_events) = database.query(filters.clone(), Order::Asc).await { - for event in cached_events { - events.push(event); - } - } - let mut relays_to_search = if let Some(client) = client { - client.get_fallback_relays().clone() - } else { - vec![] - }; - let mut relays_searched = vec![]; - let user_ref = loop { - if let Some(client) = client { - for event in client - .get_events(relays_to_search.clone(), filters.clone()) - .await - .unwrap_or(vec![]) - { - let _ = database.save_event(&event).await; - events.push(event); - } - } - #[allow(clippy::clone_on_copy)] - let user_ref = UserRef { - public_key: public_key.clone(), - metadata: extract_user_metadata(public_key, &events)?, - relays: extract_user_relays(public_key, &events), - }; + let mut events = get_event_from_global_cache(git_repo.get_path()?, filters.clone()).await?; - if client.is_none() { - break user_ref; - } - for r in &relays_to_search { - relays_searched.push(r.clone()); + if let Some(client) = client { + if events.is_empty() { + let term = console::Term::stderr(); + term.write_line("searching for profile...")?; + let (_, progress_reporter) = client + .fetch_all(git_repo.get_path()?, &HashSet::new()) + .await?; + events = get_event_from_global_cache(git_repo.get_path()?, filters).await?; + if !events.is_empty() { + progress_reporter.clear()?; + // term.clear_last_lines(1)?; + } } + } - relays_to_search = user_ref - .relays - .write() - .iter() - .filter(|r| !relays_searched.iter().any(|or| r.eq(&or))) - .map(std::clone::Clone::clone) - .collect(); - if !relays_to_search.is_empty() { - continue; + Ok(UserRef { + public_key: public_key.to_owned(), + metadata: extract_user_metadata(public_key, &events)?, + relays: extract_user_relays(public_key, &events), + }) +} + +pub async fn get_logged_in_user_and_relays_from_cache( + git_repo_path: &Path, +) -> Result<(Option, HashSet)> { + let git_repo = Repo::from_path(&git_repo_path.to_path_buf())?; + let current_user = if let Some(npub) = git_repo.get_git_config_item("nostr.npub", None)? { + if let Ok(pubic_key) = PublicKey::parse(npub) { + Some(pubic_key) + } else { + None } - break user_ref; + } else { + None + }; + let relays = if let Some(current_user) = current_user { + extract_user_relays( + ¤t_user, + &get_event_from_global_cache( + git_repo.get_path()?, + vec![ + nostr::Filter::default() + .author((*current_user).into()) + .kind(Kind::RelayList), + ], + ) + .await?, + ) + .write() + .iter() + .filter_map(|r| Url::parse(r).ok()) + .collect::>() + } else { + HashSet::new() }; - Ok(user_ref) + Ok((current_user, relays)) } diff --git a/src/sub_commands/fetch.rs b/src/sub_commands/fetch.rs index 07fd6f9..5a6850c 100644 --- a/src/sub_commands/fetch.rs +++ b/src/sub_commands/fetch.rs @@ -9,7 +9,7 @@ use crate::client::Client; #[cfg(test)] use crate::client::MockConnect; use crate::{ - client::Connect, + client::{consolidate_fetch_reports, Connect}, git::{Repo, RepoActions}, repo_ref::get_repo_coordinates, Cli, @@ -38,9 +38,16 @@ pub async fn launch(args: &Cli, command_args: &SubCommandArgs) -> Result<()> { } repo_coordinates }; - client + println!("fetching updates..."); + let (relay_reports, _) = client .fetch_all(git_repo.get_path()?, &repo_coordinates) .await?; + let report = consolidate_fetch_reports(relay_reports); + if report.to_string().is_empty() { + println!("no updates"); + } else { + println!("updates: {report}"); + } client.disconnect().await?; Ok(()) } -- cgit v1.2.3