diff --git a/crates/coop/src/views/login.rs b/crates/coop/src/views/login.rs index b48149f..624acea 100644 --- a/crates/coop/src/views/login.rs +++ b/crates/coop/src/views/login.rs @@ -22,8 +22,6 @@ use ui::notification::Notification; use ui::popup_menu::PopupMenu; use ui::{ContextModal, Disableable, Sizable, StyledExt}; -const TIMEOUT: u64 = 30; - pub fn init(window: &mut Window, cx: &mut App) -> Entity { Login::new(window, cx) } @@ -348,7 +346,7 @@ impl Login { }; let client_keys = ClientKeys::get_global(cx).keys(); - let timeout = Duration::from_secs(TIMEOUT); + let timeout = Duration::from_secs(NOSTR_CONNECT_TIMEOUT / 8); // .unwrap() is fine here because there's no error handling for bunker uri let mut signer = NostrConnect::new(uri, client_keys, timeout, None).unwrap(); // Handle auth url with the default browser @@ -356,7 +354,7 @@ impl Login { // Start countdown cx.spawn_in(window, async move |this, cx| { - for i in (0..=TIMEOUT).rev() { + for i in (0..=NOSTR_CONNECT_TIMEOUT / 8).rev() { if i == 0 { this.update(cx, |this, cx| { this.set_countdown(None, cx); diff --git a/crates/coop/src/views/startup.rs b/crates/coop/src/views/startup.rs index bcfd918..e910e7b 100644 --- a/crates/coop/src/views/startup.rs +++ b/crates/coop/src/views/startup.rs @@ -1,13 +1,15 @@ +use gpui::prelude::FluentBuilder; use gpui::{ div, svg, AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle, Focusable, IntoElement, ParentElement, Render, SharedString, Styled, Window, }; +use identity::Identity; use theme::ActiveTheme; -use ui::button::Button; +use ui::button::{Button, ButtonVariants}; use ui::dock_area::panel::{Panel, PanelEvent}; use ui::indicator::Indicator; use ui::popup_menu::PopupMenu; -use ui::Sizable; +use ui::{Sizable, StyledExt}; pub fn init(window: &mut Window, cx: &mut App) -> Entity { Startup::new(window, cx) @@ -55,7 +57,11 @@ impl Focusable for Startup { impl Render for Startup { fn render(&mut self, _window: &mut gpui::Window, cx: &mut Context) -> impl IntoElement { + let identity = Identity::global(cx); + let logging_in = identity.read(cx).logging_in(); + div() + .relative() .size_full() .flex() .items_center() @@ -80,8 +86,46 @@ impl Render for Startup { .flex() .items_center() .justify_center() + .gap_2() + .when(logging_in, |this| { + this.child( + div() + .text_sm() + .text_color(cx.theme().text) + .child("Auto login in progress"), + ) + }) .child(Indicator::new().small()), ), ) + .child( + div().absolute().bottom_3().right_3().child( + div() + .flex() + .items_center() + .justify_end() + .gap_1p5() + .child( + div() + .text_xs() + .font_semibold() + .text_color(cx.theme().text_muted) + .child("Stuck?"), + ) + .child( + Button::new("reset") + .label("Reset") + .small() + .ghost() + .on_click(|_, window, cx| { + Identity::global(cx).update(cx, |this, cx| { + this.unload(window, cx); + // Restart application + cx.restart(None); + }); + }), + ), + ), + ) } } diff --git a/crates/identity/src/lib.rs b/crates/identity/src/lib.rs index b93d2d4..7b737af 100644 --- a/crates/identity/src/lib.rs +++ b/crates/identity/src/lib.rs @@ -31,6 +31,7 @@ impl Global for GlobalIdentity {} pub struct Identity { profile: Option, + auto_logging_in_progress: bool, #[allow(dead_code)] subscriptions: SmallVec<[Subscription; 1]>, } @@ -62,6 +63,7 @@ impl Identity { // Skip auto login if the user hasn't enabled auto login if has_client_keys && auto_login { + this.set_logging_in(true, cx); this.load(window, cx); } else { this.set_profile(None, cx); @@ -71,6 +73,7 @@ impl Identity { Self { profile: None, + auto_logging_in_progress: false, subscriptions, } } @@ -167,32 +170,24 @@ impl Identity { window: &mut Window, cx: &mut Context, ) { - let timeout = Duration::from_secs(NOSTR_CONNECT_TIMEOUT); + let timeout = Duration::from_secs(NOSTR_CONNECT_TIMEOUT / 10); let client_keys = ClientKeys::get_global(cx).keys(); let Ok(mut signer) = NostrConnect::new(uri, client_keys, timeout, None) else { - window.push_notification(Notification::error("Bunker URI is invalid"), cx); + window.push_notification( + Notification::error("Bunker URI is invalid").title("Nostr Connect"), + cx, + ); self.set_profile(None, cx); return; }; // Automatically open auth url signer.auth_url_handler(CoopAuthUrlHandler); - let (tx, rx) = oneshot::channel::>(); - - // Verify the signer, make sure Remote Signer is connected - cx.background_spawn(async move { - if signer.bunker_uri().await.is_ok() { - tx.send(Some(signer)).ok(); - } else { - tx.send(None).ok(); - } - }) - .detach(); - cx.spawn_in(window, async move |this, cx| { - match rx.await { - Ok(Some(signer)) => { + // Call .bunker_uri() to verify the connection + match signer.bunker_uri().await { + Ok(_) => { cx.update(|window, cx| { this.update(cx, |this, cx| { this.set_signer(signer, window, cx); @@ -201,10 +196,10 @@ impl Identity { }) .ok(); } - _ => { + Err(e) => { cx.update(|window, cx| { window.push_notification( - Notification::error("Failed to connect to the remote signer"), + Notification::error(e.to_string()).title("Nostr Connect"), cx, ); this.update(cx, |this, cx| { @@ -512,4 +507,13 @@ impl Identity { pub fn has_profile(&self) -> bool { self.profile.is_some() } + + pub fn logging_in(&self) -> bool { + self.auto_logging_in_progress + } + + pub(crate) fn set_logging_in(&mut self, status: bool, cx: &mut Context) { + self.auto_logging_in_progress = status; + cx.notify(); + } }