diff --git a/src/overlord/mod.rs b/src/overlord/mod.rs index a796a601..599ac99a 100644 --- a/src/overlord/mod.rs +++ b/src/overlord/mod.rs @@ -408,7 +408,11 @@ impl Overlord { } "generate_private_key" => { let mut password: String = serde_json::from_str(&bus_message.json_payload)?; - GLOBALS.signer.write().await.generate_private_key(&password)?; + GLOBALS + .signer + .write() + .await + .generate_private_key(&password)?; password.zeroize(); GLOBALS.signer.read().await.save_through_settings().await?; } @@ -417,7 +421,11 @@ impl Overlord { serde_json::from_str(&bus_message.json_payload)?; let pk = PrivateKey::try_from_bech32_string(&import_bech32)?; import_bech32.zeroize(); - GLOBALS.signer.write().await.set_private_key(pk, &password)?; + GLOBALS + .signer + .write() + .await + .set_private_key(pk, &password)?; password.zeroize(); GLOBALS.signer.read().await.save_through_settings().await?; } @@ -426,10 +434,31 @@ impl Overlord { serde_json::from_str(&bus_message.json_payload)?; let pk = PrivateKey::try_from_hex_string(&import_hex)?; import_hex.zeroize(); - GLOBALS.signer.write().await.set_private_key(pk, &password)?; + GLOBALS + .signer + .write() + .await + .set_private_key(pk, &password)?; password.zeroize(); GLOBALS.signer.read().await.save_through_settings().await?; } + "import_pub" => { + let pubstr: String = serde_json::from_str(&bus_message.json_payload)?; + let maybe_pk1 = PublicKey::try_from_bech32_string(&pubstr); + let maybe_pk2 = PublicKey::try_from_hex_string(&pubstr); + if maybe_pk1.is_err() && maybe_pk2.is_err() { + *GLOBALS.status_message.write().await = + "Public key not recognized.".to_owned(); + } else { + let pubkey = maybe_pk1.unwrap_or_else(|_| maybe_pk2.unwrap()); + GLOBALS.signer.write().await.set_public_key(pubkey); + GLOBALS.signer.read().await.save_through_settings().await?; + } + } + "delete_pub" => { + GLOBALS.signer.write().await.clear_public_key(); + GLOBALS.signer.read().await.save_through_settings().await?; + } "save_relays" => { let dirty_relays: Vec = GLOBALS .relays diff --git a/src/signer.rs b/src/signer.rs index 4dc59d9a..9db6bc3c 100644 --- a/src/signer.rs +++ b/src/signer.rs @@ -3,35 +3,26 @@ use crate::globals::GLOBALS; use nostr_types::{EncryptedPrivateKey, Event, KeySecurity, PreEvent, PrivateKey, PublicKey}; use tokio::task; +#[derive(Default)] pub struct Signer { public: Option, encrypted: Option, private: Option, } -impl Default for Signer { - fn default() -> Signer { - Signer { - public: None, - encrypted: None, - private: None, - } - } -} - impl Signer { pub async fn load_from_settings(&mut self) { let settings = GLOBALS.settings.read().await; *self = Signer { public: settings.public_key, encrypted: settings.encrypted_private_key.clone(), - private: None + private: None, }; } pub async fn save_through_settings(&self) -> Result<(), Error> { let mut settings = GLOBALS.settings.write().await; - settings.public_key = self.public.clone(); + settings.public_key = self.public; settings.encrypted_private_key = self.encrypted.clone(); settings.save().await } @@ -39,12 +30,23 @@ impl Signer { #[allow(dead_code)] pub fn set_public_key(&mut self, pk: PublicKey) { if self.private.is_some() { - *GLOBALS.status_message.blocking_write() = "Ignored setting of public key (private key supercedes)".to_string(); + *GLOBALS.status_message.blocking_write() = + "Ignored setting of public key (private key supercedes)".to_string(); } else { self.public = Some(pk); } } + #[allow(dead_code)] + pub fn clear_public_key(&mut self) { + if self.private.is_some() { + *GLOBALS.status_message.blocking_write() = + "Ignored clearing of public key (private key supercedes)".to_string(); + } else { + self.public = None; + } + } + #[allow(dead_code)] pub fn set_encrypted_private_key(&mut self, epk: EncryptedPrivateKey) { if self.private.is_some() && self.encrypted.is_some() { @@ -55,7 +57,7 @@ impl Signer { } pub fn set_private_key(&mut self, pk: PrivateKey, pass: &str) -> Result<(), Error> { - self.encrypted = Some(pk.export_encrypted(&pass)?); + self.encrypted = Some(pk.export_encrypted(pass)?); self.public = Some(pk.public_key()); self.private = Some(pk); Ok(()) @@ -65,8 +67,7 @@ impl Signer { if self.private.is_some() { // ignore, already unlocked Ok(()) - } - else if let Some(epk) = &self.encrypted { + } else if let Some(epk) = &self.encrypted { self.private = Some(epk.decrypt(pass)?); Ok(()) } else { @@ -91,7 +92,7 @@ impl Signer { } pub fn public_key(&self) -> Option { - self.public.clone() + self.public } pub fn encrypted_private_key(&self) -> Option { @@ -99,18 +100,14 @@ impl Signer { } pub fn key_security(&self) -> Option { - if let Some(pk) = &self.private { - Some(pk.key_security()) - } else { - None - } + self.private.as_ref().map(|pk| pk.key_security()) } pub fn sign_preevent(&self, preevent: PreEvent, pow: Option) -> Result { match &self.private { Some(pk) => match pow { - Some(pow) => Ok(Event::new_with_pow(preevent, &pk, pow)?), - None => Ok(Event::new(preevent, &pk)?), + Some(pow) => Ok(Event::new_with_pow(preevent, pk, pow)?), + None => Ok(Event::new(preevent, pk)?), }, _ => Err(Error::NoPrivateKey), } diff --git a/src/ui/feed.rs b/src/ui/feed.rs index 4acadc20..9c27937e 100644 --- a/src/ui/feed.rs +++ b/src/ui/feed.rs @@ -72,11 +72,11 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram Err(_) => -1, }; /* - let incoming_count: isize = match GLOBALS.incoming_events.try_read() { - Ok(v) => v.len() as isize, - Err(_) => -1, -}; - */ + let incoming_count: isize = match GLOBALS.incoming_events.try_read() { + Ok(v) => v.len() as isize, + Err(_) => -1, + }; + */ ui.with_layout(Layout::right_to_left(Align::TOP), |ui| { if ui @@ -586,7 +586,8 @@ fn render_content(app: &mut GossipUi, ui: &mut Ui, tag_re: ®ex::Regex, event: } Tag::Hashtag(s) => { if ui.link(format!("#{}", s)).clicked() { - *GLOBALS.status_message.blocking_write() = "Gossip doesn't have a hashtag feed yet.".to_owned(); + *GLOBALS.status_message.blocking_write() = + "Gossip doesn't have a hashtag feed yet.".to_owned(); } } _ => { diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 041c8f1d..dc4d662a 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -84,6 +84,7 @@ struct GossipUi { password: String, import_bech32: String, import_hex: String, + import_pub: String, replying_to: Option, person_view_pubkey: Option, avatars: HashMap, @@ -154,6 +155,7 @@ impl GossipUi { password: "".to_owned(), import_bech32: "".to_owned(), import_hex: "".to_owned(), + import_pub: "".to_owned(), replying_to: None, person_view_pubkey: None, avatars: HashMap::new(), @@ -253,7 +255,10 @@ impl eframe::App for GossipUi { egui::TopBottomPanel::bottom("status").show(ctx, |ui| { ui.horizontal(|ui| { if ui - .add(Label::new(GLOBALS.status_message.blocking_read().clone()).sense(Sense::click())) + .add( + Label::new(GLOBALS.status_message.blocking_read().clone()) + .sense(Sense::click()), + ) .clicked() { *GLOBALS.status_message.blocking_write() = "".to_string(); diff --git a/src/ui/relays.rs b/src/ui/relays.rs index 5e544361..ffdec8fd 100644 --- a/src/ui/relays.rs +++ b/src/ui/relays.rs @@ -36,7 +36,8 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr &app.new_relay_url ); } else { - *GLOBALS.status_message.blocking_write() = "That's not a valid relay URL.".to_owned(); + *GLOBALS.status_message.blocking_write() = + "That's not a valid relay URL.".to_owned(); } } }); diff --git a/src/ui/you.rs b/src/ui/you.rs index 731b51cc..d3e95448 100644 --- a/src/ui/you.rs +++ b/src/ui/you.rs @@ -86,8 +86,7 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr *GLOBALS.status_message.blocking_write() = "Exported key has been printed to the console standard output.".to_owned(); } - Err(e) => - *GLOBALS.status_message.blocking_write() = format!("{}", e), + Err(e) => *GLOBALS.status_message.blocking_write() = format!("{}", e), } app.password.zeroize(); app.password = "".to_owned(); @@ -200,9 +199,7 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr app.password = "".to_owned(); } - ui.add_space(10.0); - ui.separator(); - ui.add_space(10.0); + ui.add_space(20.0); ui.heading("Import a hex private key"); @@ -230,5 +227,54 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr app.password.zeroize(); app.password = "".to_owned(); } + + ui.add_space(10.0); + ui.separator(); + ui.add_space(10.0); + + ui.heading("Public Key"); + ui.add_space(10.0); + + ui.label("You can just import your public key if you only want to view events and don't want to use gossip to create events. This will allow you to (eventually) sync your follow list and follow people on gossip without copying your private key here."); + + if let Some(pk) = GLOBALS.signer.blocking_read().public_key() { + let pkhex: PublicKeyHex = pk.into(); + ui.horizontal(|ui| { + ui.label(&format!("Public Key (Hex): {}", pkhex.0)); + if ui.add(CopyButton {}).clicked() { + ui.output().copied_text = pkhex.0; + } + }); + + if let Ok(bech32) = pk.try_as_bech32_string() { + ui.horizontal(|ui| { + ui.label(&format!("Public Key (bech32): {}", bech32)); + if ui.add(CopyButton {}).clicked() { + ui.output().copied_text = bech32; + } + }); + } + + if ui.button("Delete this public key").clicked() { + let _ = GLOBALS.to_overlord.send(BusMessage { + target: "overlord".to_string(), + kind: "delete_pub".to_string(), + json_payload: serde_json::to_string(&app.import_pub).unwrap(), + }); + } + } else { + ui.horizontal_wrapped(|ui| { + ui.label("Enter your public key"); + ui.add(TextEdit::singleline(&mut app.import_pub).hint_text("npub1 or hex")); + if ui.button("Import a Public Key").clicked() { + let _ = GLOBALS.to_overlord.send(BusMessage { + target: "overlord".to_string(), + kind: "import_pub".to_string(), + json_payload: serde_json::to_string(&app.import_pub).unwrap(), + }); + app.import_pub = "".to_owned(); + } + }); + } } }