use std::time::Duration; use anyhow::Error; use device::DeviceRegistry; use gpui::prelude::FluentBuilder; use gpui::{ AppContext, Context, Entity, IntoElement, ParentElement, Render, SharedString, Styled, Subscription, Task, Window, div, }; use nostr_sdk::prelude::*; use theme::ActiveTheme; use ui::button::{Button, ButtonVariants}; use ui::input::{Input, InputEvent, InputState}; use ui::{WindowExtension, v_flex}; #[derive(Debug)] pub struct RestoreEncryption { /// Secret key input key_input: Entity, /// Error message error: Entity>, /// Async tasks tasks: Vec>>, /// Event subscription _subscription: Option, } impl RestoreEncryption { pub fn new(window: &mut Window, cx: &mut Context) -> Self { let key_input = cx.new(|cx| InputState::new(window, cx).masked(true)); let error = cx.new(|_| None); let subscription = cx.subscribe_in(&key_input, window, |this, _input, event, window, cx| { if let InputEvent::PressEnter { .. } = event { this.restore(window, cx); }; }); Self { key_input, error, tasks: vec![], _subscription: Some(subscription), } } fn restore(&mut self, window: &mut Window, cx: &mut Context) { let device = DeviceRegistry::global(cx); let content = self.key_input.read(cx).value(); if !content.is_empty() { self.set_error("Secret Key cannot be empty.", cx); } let Ok(secret) = SecretKey::parse(&content) else { self.set_error("Secret Key is invalid.", cx); return; }; device.update(cx, |this, cx| { this.set_announcement(Keys::new(secret), cx); }); // Close the current modal window.close_modal(cx); } fn set_error(&mut self, message: S, cx: &mut Context) where S: Into, { // Update error message self.error.update(cx, |this, cx| { *this = Some(message.into()); cx.notify(); }); // Clear the error message after 3 secs self.tasks.push(cx.spawn(async move |this, cx| { cx.background_executor().timer(Duration::from_secs(3)).await; this.update(cx, |this, cx| { this.error.update(cx, |this, cx| { *this = None; cx.notify(); }); })?; Ok(()) })); } } impl Render for RestoreEncryption { fn render(&mut self, _window: &mut gpui::Window, cx: &mut Context) -> impl IntoElement { v_flex() .size_full() .gap_2() .text_sm() .child( v_flex() .gap_1() .text_sm() .text_color(cx.theme().text_muted) .child("Secret Key") .child(Input::new(&self.key_input)), ) .child( Button::new("restore") .label("Restore") .primary() .on_click(cx.listener(move |this, _, window, cx| { this.restore(window, cx); })), ) .when_some(self.error.read(cx).as_ref(), |this, error| { this.child( div() .text_xs() .text_center() .text_color(cx.theme().text_danger) .child(error.clone()), ) }) } }