refactor signer
This commit is contained in:
@@ -19,7 +19,7 @@ use nostr_sdk::prelude::*;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use smol::lock::RwLock;
|
||||
use state::{DEVICE_GIFTWRAP, NostrRegistry, USER_GIFTWRAP};
|
||||
use state::{DEVICE_GIFTWRAP, NostrRegistry, USER_GIFTWRAP, UniversalSigner};
|
||||
|
||||
mod message;
|
||||
mod room;
|
||||
@@ -140,17 +140,15 @@ impl ChatRegistry {
|
||||
/// Create a new chat registry instance
|
||||
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let user_signer = nostr.read(cx).signer.clone();
|
||||
|
||||
let (tx, rx) = flume::unbounded::<Signal>();
|
||||
let mut subscriptions = smallvec![];
|
||||
|
||||
subscriptions.push(
|
||||
// Subscribe to the signer event
|
||||
cx.observe(&user_signer, |this, signer, cx| {
|
||||
if let Some(keys) = signer.read(cx).clone() {
|
||||
cx.subscribe(&nostr, |this, _nostr, event, cx| {
|
||||
if event.signer_changed() {
|
||||
this.reset(cx);
|
||||
this.handle_notifications(keys, cx);
|
||||
this.handle_notifications(cx);
|
||||
this.get_metadata(cx);
|
||||
this.get_rooms(cx);
|
||||
};
|
||||
@@ -178,9 +176,10 @@ impl ChatRegistry {
|
||||
}
|
||||
|
||||
/// Handle nostr notifications
|
||||
fn handle_notifications(&mut self, signer: Keys, cx: &mut Context<Self>) {
|
||||
fn handle_notifications(&mut self, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let signer = nostr.read(cx).signer();
|
||||
|
||||
let tracking = self.tracking.clone();
|
||||
let msg_relays_existed = self.msg_relays_existed.clone();
|
||||
@@ -340,7 +339,7 @@ impl ChatRegistry {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let Some(public_key) = nostr.read(cx).signer_pubkey(cx) else {
|
||||
let Some(public_key) = nostr.read(cx).current_user() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -392,11 +391,9 @@ impl ChatRegistry {
|
||||
fn get_messages(&mut self, msg_relays: &Event, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let urls: Vec<RelayUrl> = nip17::extract_relay_list(msg_relays).collect();
|
||||
let signer = nostr.read(cx).signer();
|
||||
|
||||
let Some(signer) = nostr.read(cx).signer(cx) else {
|
||||
return;
|
||||
};
|
||||
let urls: Vec<RelayUrl> = nip17::extract_relay_list(msg_relays).collect();
|
||||
|
||||
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
|
||||
let public_key = signer.get_public_key_async().await?;
|
||||
@@ -514,7 +511,7 @@ impl ChatRegistry {
|
||||
{
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
|
||||
let Some(public_key) = nostr.read(cx).signer_pubkey(cx) else {
|
||||
let Some(public_key) = nostr.read(cx).current_user() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -625,7 +622,7 @@ impl ChatRegistry {
|
||||
pub fn get_rooms(&mut self, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
|
||||
let Some(public_key) = nostr.read(cx).signer_pubkey(cx) else {
|
||||
let Some(public_key) = nostr.read(cx).current_user() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -735,7 +732,7 @@ impl ChatRegistry {
|
||||
pub fn new_message(&mut self, message: NewMessage, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
|
||||
let Some(public_key) = nostr.read(cx).signer_pubkey(cx) else {
|
||||
let Some(public_key) = nostr.read(cx).current_user() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -771,7 +768,7 @@ impl ChatRegistry {
|
||||
/// Unwraps a gift-wrapped event and processes its contents.
|
||||
async fn extract_rumor(
|
||||
client: &Client,
|
||||
signer: &Keys,
|
||||
signer: &UniversalSigner,
|
||||
gift_wrap: &Event,
|
||||
) -> Result<UnsignedEvent, Error> {
|
||||
// Try to get cached rumor first
|
||||
@@ -795,7 +792,7 @@ async fn extract_rumor(
|
||||
}
|
||||
|
||||
/// Helper method to try unwrapping with different signers
|
||||
async fn try_unwrap(signer: &Keys, gift_wrap: &Event) -> Result<UnwrappedGift, Error> {
|
||||
async fn try_unwrap(signer: &UniversalSigner, gift_wrap: &Event) -> Result<UnwrappedGift, Error> {
|
||||
/*
|
||||
* // Try with the device signer first
|
||||
if let Some(signer) = signer.get_encryption_signer().await {
|
||||
@@ -808,13 +805,16 @@ async fn try_unwrap(signer: &Keys, gift_wrap: &Event) -> Result<UnwrappedGift, E
|
||||
// Fallback to the user's signer
|
||||
let user_signer = signer.get().await;
|
||||
*/
|
||||
let unwrapped = try_unwrap_with(gift_wrap, signer).await?;
|
||||
let unwrapped = try_unwrap_with(signer, gift_wrap).await?;
|
||||
|
||||
Ok(unwrapped)
|
||||
}
|
||||
|
||||
/// Attempts to unwrap a gift wrap event with a given signer.
|
||||
async fn try_unwrap_with(gift_wrap: &Event, signer: &Keys) -> Result<UnwrappedGift, Error> {
|
||||
async fn try_unwrap_with(
|
||||
signer: &UniversalSigner,
|
||||
gift_wrap: &Event,
|
||||
) -> Result<UnwrappedGift, Error> {
|
||||
// Get the sealed event
|
||||
let seal = signer
|
||||
.nip44_decrypt_async(&gift_wrap.pubkey, &gift_wrap.content)
|
||||
|
||||
@@ -10,7 +10,7 @@ use itertools::Itertools;
|
||||
use nostr_sdk::prelude::*;
|
||||
use person::{Person, PersonRegistry};
|
||||
use settings::{RoomConfig, SignerKind};
|
||||
use state::{NostrRegistry, TIMEOUT};
|
||||
use state::{NostrRegistry, TIMEOUT, UniversalSigner};
|
||||
|
||||
use crate::NewMessage;
|
||||
|
||||
@@ -427,7 +427,7 @@ impl Room {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
|
||||
// Get current user's public key
|
||||
let sender = nostr.read(cx).signer_pubkey(cx)?;
|
||||
let sender = nostr.read(cx).current_user()?;
|
||||
|
||||
// Get all members, excluding the sender
|
||||
let members: Vec<Person> = self
|
||||
@@ -482,13 +482,12 @@ impl Room {
|
||||
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let user_signer = nostr.read(cx).signer();
|
||||
let current_user = nostr.read(cx).current_user()?;
|
||||
|
||||
// Get current user's public key
|
||||
let user_signer = nostr.read(cx).signer(cx)?;
|
||||
let public_key = nostr.read(cx).signer_pubkey(cx)?;
|
||||
|
||||
// Get sender's profile
|
||||
let persons = PersonRegistry::global(cx);
|
||||
let sender = persons.read(cx).get(&public_key, cx);
|
||||
let sender = persons.read(cx).get(¤t_user, cx);
|
||||
|
||||
// Get all members (excluding sender)
|
||||
let members: Vec<Person> = self
|
||||
@@ -597,7 +596,7 @@ impl Room {
|
||||
// Helper function to send a gift-wrapped event
|
||||
async fn send_gift_wrap(
|
||||
client: &Client,
|
||||
signer: &Keys,
|
||||
signer: &UniversalSigner,
|
||||
receiver: &Person,
|
||||
rumor: &UnsignedEvent,
|
||||
config: &SignerKind,
|
||||
|
||||
@@ -15,7 +15,7 @@ use nostr_sdk::prelude::*;
|
||||
use person::PersonRegistry;
|
||||
use settings::AppSettings;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use state::{Announcement, CLIENT_NAME, NostrRegistry};
|
||||
use state::{Announcement, CLIENT_NAME, NostrRegistry, UniversalSigner};
|
||||
use theme::ActiveTheme;
|
||||
use ui::avatar::Avatar;
|
||||
use ui::button::Button;
|
||||
@@ -66,7 +66,7 @@ pub struct DeviceRegistry {
|
||||
pub announcement_existed: Arc<AtomicBool>,
|
||||
|
||||
/// Signer
|
||||
signer: Entity<Option<Keys>>,
|
||||
signer: Entity<Option<UniversalSigner>>,
|
||||
|
||||
/// Async tasks
|
||||
tasks: Vec<Task<Result<(), Error>>>,
|
||||
@@ -90,14 +90,11 @@ impl DeviceRegistry {
|
||||
|
||||
/// Create a new device registry instance
|
||||
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let signer = cx.new(|_| None);
|
||||
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let user_signer = nostr.read(cx).signer.clone();
|
||||
|
||||
let settings = AppSettings::global(cx);
|
||||
let is_nip4e_enabled = settings.read(cx).is_nip4e_enabled(cx);
|
||||
let nip4e_enabled = settings.read(cx).is_nip4e_enabled(cx);
|
||||
|
||||
let signer = cx.new(|_| None);
|
||||
let mut subscriptions = smallvec![];
|
||||
|
||||
subscriptions.push(
|
||||
@@ -111,10 +108,10 @@ impl DeviceRegistry {
|
||||
|
||||
subscriptions.push(
|
||||
// Observe the user signer
|
||||
cx.observe(&user_signer, move |this, signer, cx| {
|
||||
if signer.read(cx).is_some() && is_nip4e_enabled {
|
||||
cx.subscribe(&nostr, move |this, _nostr, event, cx| {
|
||||
if event.signer_changed() && nip4e_enabled {
|
||||
this.get_announcement(cx);
|
||||
};
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -134,7 +131,7 @@ impl DeviceRegistry {
|
||||
fn handle_notifications(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let current_user = nostr.read(cx).signer_pubkey(cx);
|
||||
let signer = nostr.read(cx).signer();
|
||||
|
||||
let announcement_existed = self.announcement_existed.clone();
|
||||
let (tx, rx) = flume::bounded::<Event>(100);
|
||||
@@ -142,6 +139,7 @@ impl DeviceRegistry {
|
||||
self.tasks.push(cx.background_spawn(async move {
|
||||
let mut notifications = client.notifications();
|
||||
let mut processed_events = HashSet::new();
|
||||
let current_user = signer.get_public_key_async().await.ok();
|
||||
|
||||
while let Some(notification) = notifications.next().await {
|
||||
if let ClientNotification::Message { message, .. } = notification
|
||||
@@ -205,14 +203,14 @@ impl DeviceRegistry {
|
||||
}
|
||||
|
||||
/// Get the signer
|
||||
pub fn signer(&self, cx: &App) -> Option<Keys> {
|
||||
pub fn signer(&self, cx: &App) -> Option<UniversalSigner> {
|
||||
self.signer.read(cx).clone()
|
||||
}
|
||||
|
||||
/// Set the decoupled encryption key for the current user
|
||||
fn set_signer(&mut self, new: Keys, cx: &mut Context<Self>) {
|
||||
fn set_signer(&mut self, new_signer: Keys, cx: &mut Context<Self>) {
|
||||
self.signer.update(cx, |this, cx| {
|
||||
*this = Some(new);
|
||||
*this = Some(UniversalSigner::new(new_signer));
|
||||
cx.notify();
|
||||
});
|
||||
cx.emit(DeviceEvent::Set);
|
||||
@@ -222,10 +220,7 @@ impl DeviceRegistry {
|
||||
pub fn backup(&self, path: PathBuf, cx: &App) -> Task<Result<(), Error>> {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let Some(signer) = nostr.read(cx).signer(cx) else {
|
||||
return Task::ready(Err(anyhow!("Signer is required")));
|
||||
};
|
||||
let signer = nostr.read(cx).signer();
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let keys = get_keys(&client, &signer).await?;
|
||||
@@ -241,13 +236,11 @@ impl DeviceRegistry {
|
||||
pub fn get_announcement(&mut self, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let Some(current_user) = nostr.read(cx).signer_pubkey(cx) else {
|
||||
return;
|
||||
};
|
||||
let signer = nostr.read(cx).signer();
|
||||
|
||||
self.tasks.push(cx.background_spawn(async move {
|
||||
let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE);
|
||||
let current_user = signer.get_public_key_async().await?;
|
||||
|
||||
// Construct the filter for the device announcement event
|
||||
let filter = Filter::new()
|
||||
@@ -317,14 +310,11 @@ impl DeviceRegistry {
|
||||
fn create_encryption(&self, keys: Keys, cx: &App) -> Task<Result<Keys, Error>> {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let signer = nostr.read(cx).signer();
|
||||
|
||||
let secret = keys.secret_key().to_secret_hex();
|
||||
let n = keys.public_key();
|
||||
|
||||
let Some(signer) = nostr.read(cx).signer(cx) else {
|
||||
return Task::ready(Err(anyhow!("Signer is required")));
|
||||
};
|
||||
|
||||
cx.background_spawn(async move {
|
||||
// Construct an announcement event
|
||||
let event = EventBuilder::new(Kind::Custom(10044), "")
|
||||
@@ -353,10 +343,7 @@ impl DeviceRegistry {
|
||||
fn set_encryption(&mut self, event: &Event, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let Some(signer) = nostr.read(cx).signer(cx) else {
|
||||
return;
|
||||
};
|
||||
let signer = nostr.read(cx).signer();
|
||||
|
||||
let announcement = Announcement::from(event);
|
||||
let device_pubkey = announcement.public_key();
|
||||
@@ -392,10 +379,7 @@ impl DeviceRegistry {
|
||||
fn wait_for_request(&mut self, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let Some(signer) = nostr.read(cx).signer(cx) else {
|
||||
return;
|
||||
};
|
||||
let signer = nostr.read(cx).signer();
|
||||
|
||||
self.tasks.push(cx.background_spawn(async move {
|
||||
let public_key = signer.get_public_key_async().await?;
|
||||
@@ -418,10 +402,7 @@ impl DeviceRegistry {
|
||||
pub fn request(&mut self, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let Some(signer) = nostr.read(cx).signer(cx) else {
|
||||
return;
|
||||
};
|
||||
let signer = nostr.read(cx).signer();
|
||||
|
||||
let Ok(app_keys) = get_or_init_app_keys(cx) else {
|
||||
return;
|
||||
@@ -486,10 +467,7 @@ impl DeviceRegistry {
|
||||
fn wait_for_approval(&mut self, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let Some(signer) = nostr.read(cx).signer(cx) else {
|
||||
return;
|
||||
};
|
||||
let signer = nostr.read(cx).signer();
|
||||
|
||||
cx.emit(DeviceEvent::Requesting);
|
||||
|
||||
@@ -554,10 +532,7 @@ impl DeviceRegistry {
|
||||
fn approve(&mut self, event: &Event, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let Some(signer) = nostr.read(cx).signer(cx) else {
|
||||
return;
|
||||
};
|
||||
let signer = nostr.read(cx).signer();
|
||||
|
||||
// Get user's write relays
|
||||
let event = event.clone();
|
||||
@@ -770,7 +745,7 @@ fn get_or_init_app_keys(cx: &App) -> Result<Keys, Error> {
|
||||
}
|
||||
|
||||
/// Encrypt and store device keys in the local database.
|
||||
async fn set_keys(client: &Client, signer: &Keys, secret: &str) -> Result<(), Error> {
|
||||
async fn set_keys(client: &Client, signer: &UniversalSigner, secret: &str) -> Result<(), Error> {
|
||||
let public_key = signer.get_public_key_async().await?;
|
||||
let content = signer.nip44_encrypt_async(&public_key, secret).await?;
|
||||
|
||||
@@ -787,7 +762,7 @@ async fn set_keys(client: &Client, signer: &Keys, secret: &str) -> Result<(), Er
|
||||
}
|
||||
|
||||
/// Get device keys from the local database.
|
||||
async fn get_keys(client: &Client, signer: &Keys) -> Result<Keys, Error> {
|
||||
async fn get_keys(client: &Client, signer: &UniversalSigner) -> Result<Keys, Error> {
|
||||
let public_key = signer.get_public_key_async().await?;
|
||||
|
||||
let filter = Filter::new()
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
[package]
|
||||
name = "relay_auth"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[dependencies]
|
||||
state = { path = "../state" }
|
||||
settings = { path = "../settings" }
|
||||
common = { path = "../common" }
|
||||
theme = { path = "../theme" }
|
||||
ui = { path = "../ui" }
|
||||
|
||||
gpui.workspace = true
|
||||
nostr-sdk.workspace = true
|
||||
|
||||
anyhow.workspace = true
|
||||
smallvec.workspace = true
|
||||
flume.workspace = true
|
||||
log.workspace = true
|
||||
@@ -1,372 +0,0 @@
|
||||
use std::borrow::Cow;
|
||||
use std::cell::Cell;
|
||||
use std::collections::HashSet;
|
||||
use std::hash::Hash;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{Context as AnyhowContext, Error, anyhow};
|
||||
use gpui::{
|
||||
App, AppContext, Context, Entity, Global, IntoElement, ParentElement, SharedString, Styled,
|
||||
Task, Window, div, relative,
|
||||
};
|
||||
use nostr_sdk::prelude::*;
|
||||
use settings::{AppSettings, AuthMode};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use state::NostrRegistry;
|
||||
use theme::ActiveTheme;
|
||||
use ui::button::Button;
|
||||
use ui::notification::{Notification, NotificationKind};
|
||||
use ui::{Disableable, WindowExtension, v_flex};
|
||||
|
||||
const AUTH_MESSAGE: &str =
|
||||
"Approve the authentication request to allow Coop to continue sending or receiving events.";
|
||||
|
||||
pub fn init(window: &mut Window, cx: &mut App) {
|
||||
RelayAuth::set_global(cx.new(|cx| RelayAuth::new(window, cx)), cx);
|
||||
}
|
||||
|
||||
/// Authentication request
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct AuthRequest {
|
||||
url: RelayUrl,
|
||||
challenge: String,
|
||||
}
|
||||
|
||||
impl AuthRequest {
|
||||
pub fn new<S>(challenge: S, url: RelayUrl) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
Self {
|
||||
challenge: challenge.into(),
|
||||
url,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn url(&self) -> &RelayUrl {
|
||||
&self.url
|
||||
}
|
||||
|
||||
pub fn challenge(&self) -> &str {
|
||||
&self.challenge
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum Signal {
|
||||
Auth(Arc<AuthRequest>),
|
||||
Pending((EventId, RelayUrl)),
|
||||
}
|
||||
|
||||
struct GlobalRelayAuth(Entity<RelayAuth>);
|
||||
|
||||
impl Global for GlobalRelayAuth {}
|
||||
|
||||
// Relay authentication
|
||||
#[derive(Debug)]
|
||||
pub struct RelayAuth {
|
||||
/// Pending events waiting for resend after authentication
|
||||
pending_events: HashSet<(EventId, RelayUrl)>,
|
||||
|
||||
/// Tasks for asynchronous operations
|
||||
_tasks: SmallVec<[Task<()>; 2]>,
|
||||
}
|
||||
|
||||
impl RelayAuth {
|
||||
/// Retrieve the global relay auth state
|
||||
pub fn global(cx: &App) -> Entity<Self> {
|
||||
cx.global::<GlobalRelayAuth>().0.clone()
|
||||
}
|
||||
|
||||
/// Set the global relay auth instance
|
||||
fn set_global(state: Entity<Self>, cx: &mut App) {
|
||||
cx.set_global(GlobalRelayAuth(state));
|
||||
}
|
||||
|
||||
/// Create a new relay auth instance
|
||||
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let mut tasks = smallvec![];
|
||||
|
||||
// Channel for communication between nostr and gpui
|
||||
let (tx, rx) = flume::bounded::<Signal>(256);
|
||||
|
||||
tasks.push(cx.background_spawn(async move {
|
||||
let mut notifications = client.notifications();
|
||||
let mut challenges: HashSet<Cow<'_, str>> = HashSet::default();
|
||||
|
||||
while let Some(notification) = notifications.next().await {
|
||||
if let ClientNotification::Message { relay_url, message } = notification {
|
||||
match *message {
|
||||
RelayMessage::Auth { challenge } => {
|
||||
if challenges.insert(challenge.clone()) {
|
||||
let request = Arc::new(AuthRequest::new(challenge, relay_url));
|
||||
let signal = Signal::Auth(request);
|
||||
|
||||
tx.send_async(signal).await.ok();
|
||||
}
|
||||
}
|
||||
RelayMessage::Ok {
|
||||
event_id, message, ..
|
||||
} => {
|
||||
let msg = MachineReadablePrefix::parse(&message);
|
||||
|
||||
// Handle authentication messages
|
||||
if let Some(MachineReadablePrefix::AuthRequired) = msg {
|
||||
let signal = Signal::Pending((event_id, relay_url));
|
||||
tx.send_async(signal).await.ok();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
tasks.push(cx.spawn_in(window, async move |this, cx| {
|
||||
while let Ok(signal) = rx.recv_async().await {
|
||||
match signal {
|
||||
Signal::Auth(req) => {
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
this.handle_auth(&req, window, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
Signal::Pending((event_id, relay_url)) => {
|
||||
this.update_in(cx, |this, _window, cx| {
|
||||
this.insert_pending_event(event_id, relay_url, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
Self {
|
||||
pending_events: HashSet::default(),
|
||||
_tasks: tasks,
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a pending event waiting for resend after authentication
|
||||
fn insert_pending_event(&mut self, id: EventId, relay: RelayUrl, cx: &mut Context<Self>) {
|
||||
self.pending_events.insert((id, relay));
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
/// Get all pending events for a specific relay,
|
||||
fn get_pending_events(&self, relay: &RelayUrl, _cx: &App) -> Vec<EventId> {
|
||||
self.pending_events
|
||||
.iter()
|
||||
.filter(|(_, pending_relay)| pending_relay == relay)
|
||||
.map(|(id, _relay)| id)
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Clear all pending events for a specific relay,
|
||||
fn clear_pending_events(&mut self, relay: &RelayUrl, cx: &mut Context<Self>) {
|
||||
self.pending_events
|
||||
.retain(|(_, pending_relay)| pending_relay != relay);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
/// Handle authentication request
|
||||
fn handle_auth(&mut self, req: &Arc<AuthRequest>, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let settings = AppSettings::global(cx);
|
||||
let trusted_relay = settings.read(cx).trusted_relay(req.url(), cx);
|
||||
let mode = AppSettings::get_auth_mode(cx);
|
||||
|
||||
if trusted_relay && mode == AuthMode::Auto {
|
||||
// Automatically authenticate if the relay is authenticated before
|
||||
self.response(req, window, cx);
|
||||
} else {
|
||||
// Otherwise open the auth request popup
|
||||
self.ask_for_approval(req, window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
/// Send auth response and wait for confirmation
|
||||
fn auth(&self, req: &Arc<AuthRequest>, cx: &App) -> Task<Result<(), Error>> {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let Some(signer) = nostr.read(cx).signer(cx) else {
|
||||
return Task::ready(Err(anyhow!("Signer is required")));
|
||||
};
|
||||
|
||||
// Get all pending events for the relay
|
||||
let req = req.clone();
|
||||
let pending_events = self.get_pending_events(req.url(), cx);
|
||||
|
||||
cx.background_spawn(async move {
|
||||
// Construct event
|
||||
let event = EventBuilder::auth(req.challenge(), req.url().clone())
|
||||
.finalize_async(&signer)
|
||||
.await?;
|
||||
|
||||
// Get the event ID
|
||||
let id = event.id;
|
||||
|
||||
// Get the relay
|
||||
let relay = client.relay(req.url()).await?.context("Relay not found")?;
|
||||
|
||||
// Subscribe to notifications
|
||||
let mut notifications = relay.notifications();
|
||||
|
||||
// Send the AUTH message
|
||||
relay
|
||||
.send_msg(ClientMessage::Auth(Cow::Borrowed(&event)))
|
||||
.await?;
|
||||
|
||||
while let Some(notification) = notifications.next().await {
|
||||
match notification {
|
||||
RelayNotification::Message { message } => {
|
||||
if let RelayMessage::Ok { event_id, .. } = *message {
|
||||
if id != event_id {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get all subscriptions
|
||||
let subscriptions = relay.subscriptions().await;
|
||||
|
||||
// Re-subscribe to previous subscriptions
|
||||
for (id, filters) in subscriptions.into_iter() {
|
||||
if !filters.is_empty() {
|
||||
relay.send_msg(ClientMessage::req(id, filters)).await?;
|
||||
}
|
||||
}
|
||||
|
||||
// Re-send pending events
|
||||
for id in pending_events {
|
||||
if let Some(event) = client.database().event_by_id(&id).await? {
|
||||
relay.send_event(&event).await?;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
RelayNotification::AuthenticationFailed => break,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Err(anyhow!("Authentication failed"))
|
||||
})
|
||||
}
|
||||
|
||||
/// Respond to an authentication request.
|
||||
fn response(&self, req: &Arc<AuthRequest>, window: &Window, cx: &Context<Self>) {
|
||||
let settings = AppSettings::global(cx);
|
||||
let req = req.clone();
|
||||
let challenge = SharedString::from(req.challenge().to_string());
|
||||
|
||||
// Create a task for authentication
|
||||
let task = self.auth(&req, cx);
|
||||
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
let result = task.await;
|
||||
let url = req.url();
|
||||
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
window.clear_notification_by_id::<AuthNotification>(challenge, cx);
|
||||
|
||||
if let Err(e) = result {
|
||||
window
|
||||
.push_notification(Notification::error(e.to_string()).autohide(false), cx);
|
||||
} else {
|
||||
// Clear pending events for the authenticated relay
|
||||
this.clear_pending_events(url, cx);
|
||||
|
||||
let domain = url.domain().unwrap_or_default();
|
||||
let msg = format!("Relay {} has been authenticated", domain);
|
||||
|
||||
window.push_notification(Notification::success(msg), cx);
|
||||
|
||||
// Save the authenticated relay to automatically authenticate future requests
|
||||
settings.update(cx, |this, cx| {
|
||||
this.add_trusted_relay(url, cx);
|
||||
});
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
/// Push a popup to approve the authentication request.
|
||||
fn ask_for_approval(&self, req: &Arc<AuthRequest>, window: &Window, cx: &Context<Self>) {
|
||||
let notification = self.notification(req, cx);
|
||||
|
||||
cx.spawn_in(window, async move |_this, cx| {
|
||||
cx.update(|window, cx| {
|
||||
window.push_notification(notification, cx);
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
/// Build a notification for the authentication request.
|
||||
fn notification(&self, req: &Arc<AuthRequest>, cx: &Context<Self>) -> Notification {
|
||||
let req = req.clone();
|
||||
let challenge = SharedString::from(req.challenge.clone());
|
||||
let url = SharedString::from(req.url().to_string());
|
||||
let entity = cx.entity().downgrade();
|
||||
let loading = Rc::new(Cell::new(false));
|
||||
|
||||
Notification::new()
|
||||
.type_id::<AuthNotification>(challenge)
|
||||
.autohide(false)
|
||||
.with_kind(NotificationKind::Info)
|
||||
.title("Authentication Required")
|
||||
.content(move |_this, _window, cx| {
|
||||
v_flex()
|
||||
.gap_2()
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.line_height(relative(1.25))
|
||||
.child(SharedString::from(AUTH_MESSAGE)),
|
||||
)
|
||||
.child(
|
||||
v_flex()
|
||||
.py_1()
|
||||
.px_1p5()
|
||||
.rounded_sm()
|
||||
.text_xs()
|
||||
.bg(cx.theme().elevated_surface_background)
|
||||
.text_color(cx.theme().text)
|
||||
.child(url.clone()),
|
||||
)
|
||||
.into_any_element()
|
||||
})
|
||||
.action(move |_this, _window, _cx| {
|
||||
let view = entity.clone();
|
||||
let req = req.clone();
|
||||
|
||||
Button::new("approve")
|
||||
.label("Approve")
|
||||
.loading(loading.get())
|
||||
.disabled(loading.get())
|
||||
.on_click({
|
||||
let loading = Rc::clone(&loading);
|
||||
move |_ev, window, cx| {
|
||||
// Set loading state to true
|
||||
loading.set(true);
|
||||
// Process to approve the request
|
||||
view.update(cx, |this, cx| {
|
||||
this.response(&req, window, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct AuthNotification;
|
||||
@@ -45,7 +45,7 @@ pub const SEARCH_RELAYS: [&str; 2] = ["wss://antiprimal.net", "wss://search.nos.
|
||||
|
||||
/// Default bootstrap relays
|
||||
pub const BOOTSTRAP_RELAYS: [&str; 3] = [
|
||||
"wss://relay.damus.io",
|
||||
"wss://relay.ditto.pub",
|
||||
"wss://relay.primal.net",
|
||||
"wss://user.kindpag.es",
|
||||
];
|
||||
|
||||
@@ -15,11 +15,13 @@ mod blossom;
|
||||
mod constants;
|
||||
mod nip05;
|
||||
mod nip4e;
|
||||
mod signer;
|
||||
|
||||
pub use blossom::*;
|
||||
pub use constants::*;
|
||||
pub use nip4e::*;
|
||||
pub use nip05::*;
|
||||
pub use signer::UniversalSigner;
|
||||
|
||||
pub fn init(window: &mut Window, cx: &mut App) {
|
||||
// rustls uses the `aws_lc_rs` provider by default
|
||||
@@ -44,6 +46,8 @@ impl Global for GlobalNostrRegistry {}
|
||||
/// Signer event.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum StateEvent {
|
||||
/// The signer has changed
|
||||
SignerChanged,
|
||||
/// Connecting to the bootstrapping relay
|
||||
Connecting,
|
||||
/// Connected to the bootstrapping relay
|
||||
@@ -59,6 +63,10 @@ impl StateEvent {
|
||||
{
|
||||
Self::Error(error.into())
|
||||
}
|
||||
|
||||
pub fn signer_changed(&self) -> bool {
|
||||
matches!(self, StateEvent::SignerChanged)
|
||||
}
|
||||
}
|
||||
|
||||
/// Nostr Registry
|
||||
@@ -67,8 +75,11 @@ pub struct NostrRegistry {
|
||||
/// Nostr client
|
||||
client: Client,
|
||||
|
||||
/// Currently active signer
|
||||
pub signer: Entity<Option<Keys>>,
|
||||
/// Universal signer
|
||||
signer: UniversalSigner,
|
||||
|
||||
/// Current user's public key
|
||||
current_user: Option<PublicKey>,
|
||||
|
||||
/// Tasks for asynchronous operations
|
||||
tasks: Vec<Task<Result<(), Error>>>,
|
||||
@@ -89,7 +100,8 @@ impl NostrRegistry {
|
||||
|
||||
/// Create a new nostr instance
|
||||
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let signer = cx.new(|_| None);
|
||||
let signer = UniversalSigner::new(Keys::generate());
|
||||
let authenticator = SignerAuthenticator::new(signer.clone());
|
||||
|
||||
// Construct the nostr lmdb instance
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -105,6 +117,7 @@ impl NostrRegistry {
|
||||
// Construct the nostr client
|
||||
let client = ClientBuilder::default()
|
||||
.database(database)
|
||||
.authenticator(authenticator)
|
||||
.gossip(NostrGossipMemory::unbounded())
|
||||
.gossip_config(GossipConfig::default().no_background_refresh())
|
||||
.connect_timeout(Duration::from_secs(10))
|
||||
@@ -121,6 +134,7 @@ impl NostrRegistry {
|
||||
Self {
|
||||
client,
|
||||
signer,
|
||||
current_user: None,
|
||||
tasks: vec![],
|
||||
}
|
||||
}
|
||||
@@ -130,22 +144,33 @@ impl NostrRegistry {
|
||||
self.client.clone()
|
||||
}
|
||||
|
||||
/// Get the signer
|
||||
pub fn signer(&self, cx: &App) -> Option<Keys> {
|
||||
self.signer.read(cx).clone()
|
||||
/// Get the current signer
|
||||
pub fn signer(&self) -> UniversalSigner {
|
||||
self.signer.clone()
|
||||
}
|
||||
|
||||
/// Get the public key of the signer
|
||||
pub fn signer_pubkey(&self, cx: &App) -> Option<PublicKey> {
|
||||
self.signer.read(cx).as_ref().map(|s| s.public_key())
|
||||
/// Get the current user's public key
|
||||
pub fn current_user(&self) -> Option<PublicKey> {
|
||||
self.current_user
|
||||
}
|
||||
|
||||
/// Set the signer to the given keys
|
||||
pub fn set_signer(&mut self, new_keys: Keys, cx: &mut Context<Self>) {
|
||||
self.signer.update(cx, |this, cx| {
|
||||
*this = Some(new_keys);
|
||||
cx.notify();
|
||||
});
|
||||
/// Update the signer
|
||||
pub fn set_signer<T>(&mut self, new_signer: T, cx: &mut Context<Self>)
|
||||
where
|
||||
T: AsyncGetPublicKey + AsyncSignEvent + AsyncNip44 + 'static,
|
||||
<T as AsyncGetPublicKey>::Error: std::error::Error + Send + Sync + 'static,
|
||||
<T as AsyncSignEvent>::Error: std::error::Error + Send + Sync + 'static,
|
||||
<T as AsyncNip44>::Error: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
cx.spawn(async move |this, cx| {
|
||||
let current_user = new_signer.get_public_key_async().await.ok();
|
||||
this.update(cx, |this, cx| {
|
||||
this.current_user = current_user;
|
||||
cx.emit(StateEvent::SignerChanged);
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
/// Connect to the bootstrapping relays
|
||||
@@ -299,10 +324,7 @@ impl NostrRegistry {
|
||||
pub fn wot_search(&self, query: &str, cx: &App) -> Task<Result<Vec<PublicKey>, Error>> {
|
||||
let client = self.client();
|
||||
let query = query.to_string();
|
||||
|
||||
let Some(signer) = self.signer.read(cx).clone() else {
|
||||
return Task::ready(Err(anyhow!("Signer is required")));
|
||||
};
|
||||
let signer = self.signer.clone();
|
||||
|
||||
cx.background_spawn(async move {
|
||||
// Construct a vertex request event
|
||||
|
||||
182
crates/state/src/signer.rs
Normal file
182
crates/state/src/signer.rs
Normal file
@@ -0,0 +1,182 @@
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use nostr_sdk::prelude::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UniversalSignerError(Box<dyn Error + Send + Sync + 'static>);
|
||||
|
||||
impl fmt::Display for UniversalSignerError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for UniversalSignerError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
Some(&*self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl UniversalSignerError {
|
||||
pub fn new<E>(err: E) -> Self
|
||||
where
|
||||
E: Error + Send + Sync + 'static,
|
||||
{
|
||||
UniversalSignerError(Box::new(err))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UniversalSigner {
|
||||
inner: Arc<RwLock<Arc<dyn InnerSigner>>>,
|
||||
}
|
||||
|
||||
impl UniversalSigner {
|
||||
pub fn new<T>(signer: T) -> Self
|
||||
where
|
||||
T: AsyncGetPublicKey + AsyncSignEvent + AsyncNip44 + 'static,
|
||||
<T as AsyncGetPublicKey>::Error: Error + Send + Sync + 'static,
|
||||
<T as AsyncSignEvent>::Error: Error + Send + Sync + 'static,
|
||||
<T as AsyncNip44>::Error: Error + Send + Sync + 'static,
|
||||
{
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(Arc::new(InnerSignerImpl(signer)))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Swap the inner signer in-place. All clones see the new signer.
|
||||
pub fn swap_inner<T>(&self, new_signer: T)
|
||||
where
|
||||
T: AsyncGetPublicKey + AsyncSignEvent + AsyncNip44 + 'static,
|
||||
<T as AsyncGetPublicKey>::Error: Error + Send + Sync + 'static,
|
||||
<T as AsyncSignEvent>::Error: Error + Send + Sync + 'static,
|
||||
<T as AsyncNip44>::Error: Error + Send + Sync + 'static,
|
||||
{
|
||||
*self.inner.write().expect("RwLock poisoned") = Arc::new(InnerSignerImpl(new_signer));
|
||||
}
|
||||
}
|
||||
|
||||
trait InnerSigner: fmt::Debug + Send + Sync + 'static {
|
||||
fn get_public_key_async(&self) -> BoxedFuture<'_, Result<PublicKey, UniversalSignerError>>;
|
||||
fn sign_event_async(
|
||||
&self,
|
||||
unsigned: UnsignedEvent,
|
||||
) -> BoxedFuture<'_, Result<Event, UniversalSignerError>>;
|
||||
fn nip44_encrypt_async<'a>(
|
||||
&'a self,
|
||||
public_key: &'a PublicKey,
|
||||
content: &'a str,
|
||||
) -> BoxedFuture<'a, Result<String, UniversalSignerError>>;
|
||||
fn nip44_decrypt_async<'a>(
|
||||
&'a self,
|
||||
public_key: &'a PublicKey,
|
||||
payload: &'a str,
|
||||
) -> BoxedFuture<'a, Result<String, UniversalSignerError>>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct InnerSignerImpl<T>(T);
|
||||
|
||||
impl<T> InnerSigner for InnerSignerImpl<T>
|
||||
where
|
||||
T: AsyncGetPublicKey + AsyncSignEvent + AsyncNip44 + Send + Sync + 'static,
|
||||
<T as AsyncGetPublicKey>::Error: Error + Send + Sync + 'static,
|
||||
<T as AsyncSignEvent>::Error: Error + Send + Sync + 'static,
|
||||
<T as AsyncNip44>::Error: Error + Send + Sync + 'static,
|
||||
{
|
||||
fn get_public_key_async(&self) -> BoxedFuture<'_, Result<PublicKey, UniversalSignerError>> {
|
||||
Box::pin(async move {
|
||||
AsyncGetPublicKey::get_public_key_async(&self.0)
|
||||
.await
|
||||
.map_err(UniversalSignerError::new)
|
||||
})
|
||||
}
|
||||
|
||||
fn sign_event_async(
|
||||
&self,
|
||||
unsigned: UnsignedEvent,
|
||||
) -> BoxedFuture<'_, Result<Event, UniversalSignerError>> {
|
||||
Box::pin(async move {
|
||||
AsyncSignEvent::sign_event_async(&self.0, unsigned)
|
||||
.await
|
||||
.map_err(UniversalSignerError::new)
|
||||
})
|
||||
}
|
||||
|
||||
fn nip44_encrypt_async<'a>(
|
||||
&'a self,
|
||||
public_key: &'a PublicKey,
|
||||
content: &'a str,
|
||||
) -> BoxedFuture<'a, Result<String, UniversalSignerError>> {
|
||||
Box::pin(async move {
|
||||
AsyncNip44::nip44_encrypt_async(&self.0, public_key, content)
|
||||
.await
|
||||
.map_err(UniversalSignerError::new)
|
||||
})
|
||||
}
|
||||
|
||||
fn nip44_decrypt_async<'a>(
|
||||
&'a self,
|
||||
public_key: &'a PublicKey,
|
||||
payload: &'a str,
|
||||
) -> BoxedFuture<'a, Result<String, UniversalSignerError>> {
|
||||
Box::pin(async move {
|
||||
AsyncNip44::nip44_decrypt_async(&self.0, public_key, payload)
|
||||
.await
|
||||
.map_err(UniversalSignerError::new)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl UniversalSigner {
|
||||
#[allow(dead_code)]
|
||||
fn with_inner<R>(&self, f: impl FnOnce(&dyn InnerSigner) -> R) -> R {
|
||||
let guard = self.inner.read().expect("RwLock poisoned");
|
||||
f(&**guard)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncGetPublicKey for UniversalSigner {
|
||||
type Error = UniversalSignerError;
|
||||
|
||||
fn get_public_key_async(&self) -> BoxedFuture<'_, Result<PublicKey, Self::Error>> {
|
||||
let inner = self.inner.read().expect("RwLock poisoned").clone();
|
||||
Box::pin(async move { inner.get_public_key_async().await })
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncSignEvent for UniversalSigner {
|
||||
type Error = UniversalSignerError;
|
||||
|
||||
fn sign_event_async(
|
||||
&self,
|
||||
unsigned: UnsignedEvent,
|
||||
) -> BoxedFuture<'_, Result<Event, Self::Error>> {
|
||||
let inner = self.inner.read().expect("RwLock poisoned").clone();
|
||||
Box::pin(async move { inner.sign_event_async(unsigned).await })
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncNip44 for UniversalSigner {
|
||||
type Error = UniversalSignerError;
|
||||
|
||||
fn nip44_encrypt_async<'a>(
|
||||
&'a self,
|
||||
public_key: &'a PublicKey,
|
||||
content: &'a str,
|
||||
) -> BoxedFuture<'a, Result<String, Self::Error>> {
|
||||
let inner = self.inner.read().expect("RwLock poisoned").clone();
|
||||
Box::pin(async move { inner.nip44_encrypt_async(public_key, content).await })
|
||||
}
|
||||
|
||||
fn nip44_decrypt_async<'a>(
|
||||
&'a self,
|
||||
public_key: &'a PublicKey,
|
||||
payload: &'a str,
|
||||
) -> BoxedFuture<'a, Result<String, Self::Error>> {
|
||||
let inner = self.inner.read().expect("RwLock poisoned").clone();
|
||||
Box::pin(async move { inner.nip44_decrypt_async(public_key, payload).await })
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ chat_ui = { path = "../chat_ui" }
|
||||
settings = { path = "../settings" }
|
||||
auto_update = { path = "../auto_update" }
|
||||
person = { path = "../person" }
|
||||
relay_auth = { path = "../relay_auth" }
|
||||
|
||||
gpui.workspace = true
|
||||
nostr-sdk.workspace = true
|
||||
|
||||
@@ -78,7 +78,7 @@ impl Screening {
|
||||
let client = nostr.read(cx).client();
|
||||
let public_key = self.public_key;
|
||||
|
||||
let Some(current_user) = nostr.read(cx).signer_pubkey(cx) else {
|
||||
let Some(current_user) = nostr.read(cx).current_user() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -106,7 +106,7 @@ impl Screening {
|
||||
let client = nostr.read(cx).client();
|
||||
let public_key = self.public_key;
|
||||
|
||||
let Some(current_user) = nostr.read(cx).signer_pubkey(cx) else {
|
||||
let Some(current_user) = nostr.read(cx).current_user() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -224,12 +224,9 @@ impl Screening {
|
||||
fn report(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let signer = nostr.read(cx).signer();
|
||||
let public_key = self.public_key;
|
||||
|
||||
let Some(signer) = nostr.read(cx).signer(cx) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
|
||||
let tag = Nip56Tag::PublicKey {
|
||||
public_key,
|
||||
|
||||
@@ -75,7 +75,6 @@ impl Workspace {
|
||||
let chat = ChatRegistry::global(cx);
|
||||
let device = DeviceRegistry::global(cx);
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let signer = nostr.read(cx).signer.clone();
|
||||
|
||||
let dock = cx.new(|cx| DockArea::new(window, cx));
|
||||
let image_cache = CoopImageCache::new(IMAGE_CACHE_SIZE, cx);
|
||||
@@ -89,20 +88,9 @@ impl Workspace {
|
||||
}),
|
||||
);
|
||||
|
||||
subscriptions.push(
|
||||
// Observe the signer
|
||||
cx.observe_in(&signer, window, |this, signer, window, cx| {
|
||||
if signer.read(cx).is_some() {
|
||||
this.set_center_layout(window, cx);
|
||||
} else {
|
||||
this.import_identity(window, cx);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
subscriptions.push(
|
||||
// Subscribe to the nostr events
|
||||
cx.subscribe_in(&nostr, window, move |this, state, event, window, cx| {
|
||||
cx.subscribe_in(&nostr, window, move |_this, _state, event, window, cx| {
|
||||
match event {
|
||||
StateEvent::Connecting => {
|
||||
let note = Notification::new()
|
||||
@@ -119,10 +107,6 @@ impl Workspace {
|
||||
.with_kind(NotificationKind::Success);
|
||||
|
||||
window.push_notification(note, cx);
|
||||
|
||||
if state.read(cx).signer.read(cx).is_none() {
|
||||
this.import_identity(window, cx);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
@@ -330,7 +314,7 @@ impl Workspace {
|
||||
Command::ShowProfile => {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
|
||||
if let Some(public_key) = nostr.read(cx).signer_pubkey(cx) {
|
||||
if let Some(public_key) = nostr.read(cx).current_user() {
|
||||
self.dock.update(cx, |this, cx| {
|
||||
this.add_panel(
|
||||
Arc::new(profile::init(public_key, window, cx)),
|
||||
@@ -583,7 +567,7 @@ impl Workspace {
|
||||
|
||||
fn titlebar_left(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let current_user = nostr.read(cx).signer_pubkey(cx);
|
||||
let current_user = nostr.read(cx).current_user();
|
||||
|
||||
h_flex()
|
||||
.flex_shrink_0()
|
||||
@@ -661,7 +645,7 @@ impl Workspace {
|
||||
let is_nip4e_enabled = AppSettings::get_nip4e(cx);
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
|
||||
let Some(public_key) = nostr.read(cx).signer_pubkey(cx) else {
|
||||
let Some(public_key) = nostr.read(cx).current_user() else {
|
||||
return div();
|
||||
};
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ impl ContactListPanel {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let Some(public_key) = nostr.read(cx).signer_pubkey(cx) else {
|
||||
let Some(public_key) = nostr.read(cx).current_user() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -157,10 +157,7 @@ impl ContactListPanel {
|
||||
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let Some(signer) = nostr.read(cx).signer(cx) else {
|
||||
return;
|
||||
};
|
||||
let signer = nostr.read(cx).signer();
|
||||
|
||||
// Get contacts
|
||||
let contacts: Vec<Contact> = self
|
||||
|
||||
@@ -31,7 +31,7 @@ impl GreeterPanel {
|
||||
fn add_profile_panel(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
|
||||
if let Some(public_key) = nostr.read(cx).signer_pubkey(cx) {
|
||||
if let Some(public_key) = nostr.read(cx).current_user() {
|
||||
cx.spawn_in(window, async move |_this, cx| {
|
||||
cx.update(|window, cx| {
|
||||
Workspace::add_panel(
|
||||
|
||||
@@ -83,7 +83,7 @@ impl MessagingRelayPanel {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let Some(public_key) = nostr.read(cx).signer_pubkey(cx) else {
|
||||
let Some(public_key) = nostr.read(cx).current_user() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -171,10 +171,7 @@ impl MessagingRelayPanel {
|
||||
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let Some(signer) = nostr.read(cx).signer(cx) else {
|
||||
return;
|
||||
};
|
||||
let signer = nostr.read(cx).signer();
|
||||
|
||||
// Construct event tags
|
||||
let tags: Vec<Tag> = self
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Context as AnyhowContext, Error, anyhow};
|
||||
use anyhow::{Context as AnyhowContext, Error};
|
||||
use gpui::{
|
||||
AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter, FocusHandle,
|
||||
Focusable, IntoElement, ParentElement, PathPromptOptions, Render, SharedString, Styled, Task,
|
||||
@@ -207,12 +207,9 @@ impl ProfilePanel {
|
||||
fn publish(&self, metadata: &Metadata, cx: &App) -> Task<Result<(), Error>> {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let signer = nostr.read(cx).signer();
|
||||
let metadata = metadata.clone();
|
||||
|
||||
let Some(signer) = nostr.read(cx).signer(cx) else {
|
||||
return Task::ready(Err(anyhow!("Signer is required")));
|
||||
};
|
||||
|
||||
cx.background_spawn(async move {
|
||||
// Build and sign the metadata event
|
||||
let event = metadata.finalize_async(&signer).await?;
|
||||
|
||||
@@ -100,7 +100,7 @@ impl RelayListPanel {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let Some(public_key) = nostr.read(cx).signer_pubkey(cx) else {
|
||||
let Some(public_key) = nostr.read(cx).current_user() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -207,10 +207,7 @@ impl RelayListPanel {
|
||||
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let Some(signer) = nostr.read(cx).signer(cx) else {
|
||||
return;
|
||||
};
|
||||
let signer = nostr.read(cx).signer();
|
||||
|
||||
// Get all relays
|
||||
let relays = self.relays.clone();
|
||||
|
||||
@@ -159,7 +159,7 @@ impl Sidebar {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let Some(public_key) = nostr.read(cx).signer_pubkey(cx) else {
|
||||
let Some(public_key) = nostr.read(cx).current_user() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -320,7 +320,7 @@ impl Sidebar {
|
||||
let async_chat = chat.downgrade();
|
||||
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let Some(public_key) = nostr.read(cx).signer_pubkey(cx) else {
|
||||
let Some(public_key) = nostr.read(cx).current_user() else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user