Skip to content

Commit

Permalink
Better setup for new profiles
Browse files Browse the repository at this point in the history
  • Loading branch information
benthecarman committed Apr 10, 2024
1 parent 5fd1159 commit 70c5724
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 7 deletions.
2 changes: 1 addition & 1 deletion mutiny-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2165,7 +2165,7 @@ impl<S: MutinyStorage> MutinyWallet<S> {
async fn sync_nostr_profile(&self) -> Result<(), MutinyError> {
let npub = self.nostr.get_npub().await;
if let Some(metadata) = self.nostr.primal_client.get_user_profile(npub).await? {
self.storage.set_nostr_profile(metadata)?;
self.storage.set_nostr_profile(&metadata)?;
}

Ok(())
Expand Down
113 changes: 108 additions & 5 deletions mutiny-core/src/nostr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ use nostr::nips::nip47::*;
use nostr::{
nips::nip04::{decrypt, encrypt},
Alphabet, Event, EventBuilder, EventId, Filter, JsonUtil, Keys, Kind, Metadata, SecretKey,
SingleLetterTag, Tag, TagKind, Timestamp,
SingleLetterTag, Tag, TagKind, Timestamp, UncheckedUrl,
};
use nostr_sdk::{Client, NostrSigner, RelayPoolNotification};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::collections::{HashMap, HashSet};
use std::sync::{atomic::Ordering, Arc, RwLock};
use std::time::Duration;
Expand Down Expand Up @@ -394,7 +395,7 @@ impl<S: MutinyStorage> NostrManager<S> {
};

let with_name = if let Some(name) = name {
current.name(name)
current.name(name).display_name(name)
} else {
current
};
Expand All @@ -420,11 +421,98 @@ impl<S: MutinyStorage> NostrManager<S> {

let event_id = self.client.set_metadata(&with_nip05).await?;
log_info!(self.logger, "New kind 0: {event_id}");
self.storage.set_nostr_profile(with_nip05.clone())?;
self.storage.set_nostr_profile(&with_nip05)?;

Ok(with_nip05)
}

/// This should only be called when the user is setting up a new profile
/// never for an existing profile
pub async fn setup_new_profile(
&self,
name: Option<String>,
img_url: Option<Url>,
lnurl: Option<LnUrl>,
nip05: Option<String>,
) -> Result<Metadata, MutinyError> {
// pull latest profile from primal
let npub = self.get_npub().await;
match self.primal_client.get_user_profile(npub).await {
Ok(Some(_)) => {
log_error!(
self.logger,
"User already has a profile, can't setup new profile"
);
return Err(MutinyError::InvalidArgumentsError);
}
Ok(None) => (),
Err(e) => {
// if we can't get the profile from primal, fall back to local
// otherwise we can't create profile if the primal server is down
log_error!(
self.logger,
"Failed to get user profile from primal, falling back to local: {e}"
);
if self.storage.get_nostr_profile()?.is_some() {
return Err(MutinyError::InvalidArgumentsError);
}
}
};

// create follow/relays list
{
// make sure we follow ourselves as well, makes other nostr feeds better
let tags = [Tag::public_key(npub)];
// set relay list in the content properly
let content: HashMap<String, Value> = RELAYS
.iter()
.map(|relay| {
let value = json!({"read":true,"write":true});
(relay.to_string(), value)
})
.collect();
let builder = EventBuilder::new(Kind::ContactList, json!(content).to_string(), tags);
self.client.send_event_builder(builder).await?;
}

// create real relay list
{
let builder = EventBuilder::relay_list(
RELAYS
.iter()
.map(|x| (UncheckedUrl::from(x.to_string()), None)),
);
self.client.send_event_builder(builder).await?;
}

// create metadata
let metadata = {
let name = name.unwrap_or_else(|| "Anon".to_string());
let mut m = Metadata::default().name(name.clone()).display_name(name);

if let Some(img_url) = img_url {
m = m.picture(img_url)
};
if let Some(lnurl) = lnurl {
if let Some(ln_addr) = lnurl.lightning_address() {
m = m.lud16(ln_addr.to_string())
} else {
m = m.lud06(lnurl.to_string())
}
};
if let Some(nip05) = nip05 {
m = m.nip05(nip05)
};
m
};

let event_id = self.client.set_metadata(&metadata).await?;
log_info!(self.logger, "New kind 0: {event_id}");
self.storage.set_nostr_profile(&metadata)?;

Ok(metadata)
}

/// Sets the user's nostr profile metadata as deleted
pub async fn delete_profile(&self) -> Result<Metadata, MutinyError> {
let metadata = Metadata::default()
Expand All @@ -435,7 +523,7 @@ impl<S: MutinyStorage> NostrManager<S> {

let event_id = self.client.set_metadata(&metadata).await?;
log_info!(self.logger, "New kind 0: {event_id}");
self.storage.set_nostr_profile(metadata.clone())?;
self.storage.set_nostr_profile(&metadata)?;

Ok(metadata)
}
Expand Down Expand Up @@ -481,7 +569,22 @@ impl<S: MutinyStorage> NostrManager<S> {
tags.push(Tag::public_key(npub));
EventBuilder::new(Kind::ContactList, content, tags)
}
None => EventBuilder::new(Kind::ContactList, "", [Tag::public_key(npub)]),
None => {
// make sure we follow ourselves as well, makes other nostr feeds better
let tags = [
Tag::public_key(npub),
Tag::public_key(self.get_npub().await),
];
// set relay list in the content properly
let content: HashMap<String, Value> = RELAYS
.iter()
.map(|relay| {
let value = json!({"read":true,"write":true});
(relay.to_string(), value)
})
.collect();
EventBuilder::new(Kind::ContactList, json!(content).to_string(), tags)
}
};

let event = self
Expand Down
2 changes: 1 addition & 1 deletion mutiny-core/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,7 @@ pub trait MutinyStorage: Clone + Sized + Send + Sync + 'static {
self.get_data(NOSTR_PROFILE_METADATA)
}

fn set_nostr_profile(&self, metadata: Metadata) -> Result<(), MutinyError> {
fn set_nostr_profile(&self, metadata: &Metadata) -> Result<(), MutinyError> {
self.set_data(NOSTR_PROFILE_METADATA.to_string(), metadata, None)
}

Expand Down
31 changes: 31 additions & 0 deletions mutiny-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1873,6 +1873,37 @@ impl MutinyWallet {
Ok(JsValue::from_serde(&profile)?)
}

/// This should only be called when the user is setting up a new profile
/// never for an existing profile
pub async fn setup_new_profile(
&self,
name: Option<String>,
img_url: Option<String>,
lnurl: Option<String>,
nip05: Option<String>,
) -> Result<JsValue, MutinyJsError> {
let img_url = img_url
.map(|i| nostr::Url::from_str(&i))
.transpose()
.map_err(|_| MutinyJsError::InvalidArgumentsError)?;

let lnurl = lnurl
.map(|l| {
LightningAddress::from_str(&l)
.map(|a| a.lnurl())
.or(LnUrl::from_str(&l))
})
.transpose()
.map_err(|_| MutinyJsError::InvalidArgumentsError)?;

let profile = self
.inner
.nostr
.setup_new_profile(name, img_url, lnurl, nip05)
.await?;
Ok(JsValue::from_serde(&profile)?)
}

/// Sets the user's nostr profile data
#[wasm_bindgen]
pub async fn edit_nostr_profile(
Expand Down

0 comments on commit 70c5724

Please sign in to comment.