wip
Some checks failed
Rust / build (ubuntu-latest, stable) (push) Failing after 1m27s
Rust / build (ubuntu-latest, stable) (pull_request) Failing after 1m33s
Some checks failed
Rust / build (ubuntu-latest, stable) (push) Failing after 1m27s
Rust / build (ubuntu-latest, stable) (pull_request) Failing after 1m33s
This commit is contained in:
@@ -12,8 +12,6 @@ use ui::Root;
|
|||||||
use crate::actions::Quit;
|
use crate::actions::Quit;
|
||||||
|
|
||||||
mod actions;
|
mod actions;
|
||||||
mod login;
|
|
||||||
mod new_identity;
|
|
||||||
mod sidebar;
|
mod sidebar;
|
||||||
mod user;
|
mod user;
|
||||||
mod views;
|
mod views;
|
||||||
|
|||||||
@@ -2,5 +2,4 @@ pub mod compose;
|
|||||||
pub mod preferences;
|
pub mod preferences;
|
||||||
pub mod screening;
|
pub mod screening;
|
||||||
pub mod setup_relay;
|
pub mod setup_relay;
|
||||||
pub mod startup;
|
|
||||||
pub mod welcome;
|
pub mod welcome;
|
||||||
|
|||||||
@@ -5,28 +5,20 @@ use gpui::{
|
|||||||
};
|
};
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
use ui::dock_area::panel::{Panel, PanelEvent};
|
use ui::dock_area::panel::{Panel, PanelEvent};
|
||||||
use ui::{v_flex, StyledExt};
|
use ui::{h_flex, v_flex, StyledExt};
|
||||||
|
|
||||||
pub fn init(window: &mut Window, cx: &mut App) -> Entity<Welcome> {
|
pub fn init(window: &mut Window, cx: &mut App) -> Entity<Welcome> {
|
||||||
Welcome::new(window, cx)
|
cx.new(|cx| Welcome::new(window, cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Welcome {
|
pub struct Welcome {
|
||||||
name: SharedString,
|
name: SharedString,
|
||||||
version: SharedString,
|
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Welcome {
|
impl Welcome {
|
||||||
pub fn new(window: &mut Window, cx: &mut App) -> Entity<Self> {
|
fn new(_window: &mut Window, cx: &mut App) -> Self {
|
||||||
cx.new(|cx| Self::view(window, cx))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn view(_window: &mut Window, cx: &mut Context<Self>) -> Self {
|
|
||||||
let version = SharedString::from(format!("Version: {}", env!("CARGO_PKG_VERSION")));
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
version,
|
|
||||||
name: "Welcome".into(),
|
name: "Welcome".into(),
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
}
|
}
|
||||||
@@ -39,12 +31,19 @@ impl Panel for Welcome {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self, cx: &App) -> AnyElement {
|
fn title(&self, cx: &App) -> AnyElement {
|
||||||
div()
|
h_flex()
|
||||||
|
.gap_1p5()
|
||||||
.child(
|
.child(
|
||||||
svg()
|
svg()
|
||||||
.path("brand/coop.svg")
|
.path("brand/coop.svg")
|
||||||
.size_4()
|
.size_4()
|
||||||
.text_color(cx.theme().element_background),
|
.text_color(cx.theme().text_muted),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_sm()
|
||||||
|
.text_color(cx.theme().text_muted)
|
||||||
|
.child(self.name.clone()),
|
||||||
)
|
)
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
@@ -92,7 +91,6 @@ impl Render for Welcome {
|
|||||||
.id("version")
|
.id("version")
|
||||||
.text_color(cx.theme().text_placeholder)
|
.text_color(cx.theme().text_placeholder)
|
||||||
.text_xs()
|
.text_xs()
|
||||||
.child(self.version.clone())
|
|
||||||
.on_click(|_, _window, cx| {
|
.on_click(|_, _window, cx| {
|
||||||
cx.open_url("https://github.com/lumehq/coop/releases");
|
cx.open_url("https://github.com/lumehq/coop/releases");
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ pub struct Identity {
|
|||||||
/// The public key of the account
|
/// The public key of the account
|
||||||
pub public_key: Option<PublicKey>,
|
pub public_key: Option<PublicKey>,
|
||||||
|
|
||||||
|
/// Whether the identity is owned by the user
|
||||||
|
pub owned: bool,
|
||||||
|
|
||||||
/// Status of the current user NIP-65 relays
|
/// Status of the current user NIP-65 relays
|
||||||
relay_list: RelayState,
|
relay_list: RelayState,
|
||||||
|
|
||||||
@@ -37,6 +40,7 @@ impl Identity {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
public_key: None,
|
public_key: None,
|
||||||
|
owned: false,
|
||||||
relay_list: RelayState::default(),
|
relay_list: RelayState::default(),
|
||||||
messaging_relays: RelayState::default(),
|
messaging_relays: RelayState::default(),
|
||||||
}
|
}
|
||||||
@@ -83,4 +87,9 @@ impl Identity {
|
|||||||
pub fn unset_public_key(&mut self) {
|
pub fn unset_public_key(&mut self) {
|
||||||
self.public_key = None;
|
self.public_key = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets whether the identity is owned by the user.
|
||||||
|
pub fn set_owned(&mut self, owned: bool) {
|
||||||
|
self.owned = owned;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::Error;
|
use anyhow::{anyhow, Error};
|
||||||
use common::{config_dir, BOOTSTRAP_RELAYS, CLIENT_NAME, SEARCH_RELAYS};
|
use common::{config_dir, BOOTSTRAP_RELAYS, CLIENT_NAME, SEARCH_RELAYS};
|
||||||
use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task};
|
use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task};
|
||||||
use nostr_connect::prelude::*;
|
use nostr_connect::prelude::*;
|
||||||
@@ -173,6 +174,14 @@ impl NostrRegistry {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
cx.defer(|cx| {
|
||||||
|
let nostr = NostrRegistry::global(cx);
|
||||||
|
|
||||||
|
nostr.update(cx, |this, cx| {
|
||||||
|
this.get_identity(cx);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
client,
|
client,
|
||||||
app_keys,
|
app_keys,
|
||||||
@@ -305,9 +314,15 @@ impl NostrRegistry {
|
|||||||
let keys = Keys::generate();
|
let keys = Keys::generate();
|
||||||
let secret_key = keys.secret_key();
|
let secret_key = keys.secret_key();
|
||||||
|
|
||||||
|
// Create directory and write secret key
|
||||||
std::fs::create_dir_all(dir.parent().unwrap())?;
|
std::fs::create_dir_all(dir.parent().unwrap())?;
|
||||||
std::fs::write(&dir, secret_key.to_secret_bytes())?;
|
std::fs::write(&dir, secret_key.to_secret_bytes())?;
|
||||||
|
|
||||||
|
// Set permissions to readonly
|
||||||
|
let mut perms = std::fs::metadata(&dir)?.permissions();
|
||||||
|
perms.set_mode(0o400);
|
||||||
|
std::fs::set_permissions(&dir, perms)?;
|
||||||
|
|
||||||
return Ok(keys);
|
return Ok(keys);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -390,7 +405,7 @@ impl NostrRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set the signer for the nostr client and verify the public key
|
/// Set the signer for the nostr client and verify the public key
|
||||||
pub fn set_signer<T>(&mut self, signer: T, cx: &mut Context<Self>)
|
pub fn set_signer<T>(&mut self, signer: T, owned: bool, cx: &mut Context<Self>)
|
||||||
where
|
where
|
||||||
T: NostrSigner + 'static,
|
T: NostrSigner + 'static,
|
||||||
{
|
{
|
||||||
@@ -414,6 +429,7 @@ impl NostrRegistry {
|
|||||||
Ok(public_key) => {
|
Ok(public_key) => {
|
||||||
identity.update(cx, |this, cx| {
|
identity.update(cx, |this, cx| {
|
||||||
this.set_public_key(public_key);
|
this.set_public_key(public_key);
|
||||||
|
this.set_owned(owned);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
@@ -598,8 +614,102 @@ impl NostrRegistry {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Store a connection for future uses
|
/// Get local stored identity
|
||||||
pub fn persit_connection(&mut self, uri: NostrConnectUri, cx: &mut App) {
|
fn get_identity(&mut self, cx: &mut Context<Self>) {
|
||||||
|
let read_credential = cx.read_credentials(CLIENT_NAME);
|
||||||
|
|
||||||
|
self.tasks.push(cx.spawn(async move |this, cx| {
|
||||||
|
match read_credential.await {
|
||||||
|
Ok(Some((_, secret))) => {
|
||||||
|
let secret = SecretKey::from_slice(&secret)?;
|
||||||
|
let keys = Keys::new(secret);
|
||||||
|
|
||||||
|
this.update(cx, |this, cx| {
|
||||||
|
this.set_signer(keys, false, cx);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
this.update(cx, |this, cx| {
|
||||||
|
this.get_bunker(cx);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new identity
|
||||||
|
fn create_identity(&mut self, cx: &mut Context<Self>) {
|
||||||
|
let keys = Keys::generate();
|
||||||
|
let write_credential = cx.write_credentials(
|
||||||
|
CLIENT_NAME,
|
||||||
|
&keys.public_key().to_hex(),
|
||||||
|
&keys.secret_key().to_secret_bytes(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update the signer
|
||||||
|
self.set_signer(keys, false, cx);
|
||||||
|
|
||||||
|
// Spawn a task to write the credentials
|
||||||
|
cx.background_spawn(async move {
|
||||||
|
if let Err(e) = write_credential.await {
|
||||||
|
log::error!("Failed to write credentials: {}", e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get local stored bunker connection
|
||||||
|
fn get_bunker(&mut self, cx: &mut Context<Self>) {
|
||||||
|
let client = self.client();
|
||||||
|
let app_keys = self.app_keys().clone();
|
||||||
|
let timeout = Duration::from_secs(NOSTR_CONNECT_TIMEOUT);
|
||||||
|
|
||||||
|
let task: Task<Result<NostrConnect, Error>> = cx.background_spawn(async move {
|
||||||
|
log::info!("Getting bunker connection");
|
||||||
|
|
||||||
|
let filter = Filter::new()
|
||||||
|
.kind(Kind::ApplicationSpecificData)
|
||||||
|
.identifier("coop:account")
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if let Some(event) = client.database().query(filter).await?.first_owned() {
|
||||||
|
let uri = NostrConnectUri::parse(event.content)?;
|
||||||
|
let signer = NostrConnect::new(uri.clone(), app_keys.clone(), timeout, None)?;
|
||||||
|
|
||||||
|
Ok(signer)
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("No account found"))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.tasks.push(cx.spawn(async move |this, cx| {
|
||||||
|
match task.await {
|
||||||
|
Ok(signer) => {
|
||||||
|
this.update(cx, |this, cx| {
|
||||||
|
this.set_signer(signer, true, cx);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Failed to get bunker: {e}");
|
||||||
|
// Create a new identity if no stored bunker exists
|
||||||
|
this.update(cx, |this, cx| {
|
||||||
|
this.create_identity(cx);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Store the bunker connection for the next login
|
||||||
|
pub fn persist_bunker(&mut self, uri: NostrConnectUri, cx: &mut App) {
|
||||||
let client = self.client();
|
let client = self.client();
|
||||||
let rng_keys = Keys::generate();
|
let rng_keys = Keys::generate();
|
||||||
|
|
||||||
@@ -632,7 +742,10 @@ impl NostrRegistry {
|
|||||||
let uri = NostrConnectUri::client(app_keys.public_key(), vec![relay], CLIENT_NAME);
|
let uri = NostrConnectUri::client(app_keys.public_key(), vec![relay], CLIENT_NAME);
|
||||||
|
|
||||||
// Generate the nostr connect
|
// Generate the nostr connect
|
||||||
let signer = NostrConnect::new(uri.clone(), app_keys.clone(), timeout, None).unwrap();
|
let mut signer = NostrConnect::new(uri.clone(), app_keys.clone(), timeout, None).unwrap();
|
||||||
|
|
||||||
|
// Handle the auth request
|
||||||
|
signer.auth_url_handler(CoopAuthUrlHandler);
|
||||||
|
|
||||||
(signer, uri)
|
(signer, uri)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user