diff --git a/locales/en.yml b/locales/en.yml index b3a21a4..46ea51c 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -7,6 +7,7 @@ retry: Retry close: Close change: Change show: Show +delete: Delete wallets: title: Wallets create_desc: Create or import existing wallet from saved recovery phrase. @@ -51,7 +52,7 @@ wallets: repair_desc: Check a wallet, repairing and restoring missing outputs if required. This operation will take time. repair_unavailable: You need an active connection to the node and completed wallet synchronization. delete: Delete wallet - delete_conf: 'Are you sure you want to remove the wallet? Enter password to confirm:' + delete_conf: Are you sure you want to delete the wallet? delete_desc: Make sure you have saved your recovery phrase to access funds in the future. wallet_loading_err: 'An error occurred during synchronization of the wallet, you can retry or change connection settings by selecting %{settings} at the bottom of the screen.' wallet: Wallet diff --git a/locales/ru.yml b/locales/ru.yml index 5b49964..2698836 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -7,6 +7,7 @@ retry: Повторить close: Закрыть change: Изменить show: Показать +delete: Удалить wallets: title: Кошельки create_desc: Создайте или импортируйте существующий кошелёк из сохранённой фразы восстановления. @@ -51,7 +52,7 @@ wallets: repair_desc: Проверить кошелёк, исправляя и восстанавливая недостающие выходы, если это необходимо. Эта операция займёт время. repair_unavailable: Необходимо активное подключение к узлу и завершённая синхронизация кошелька. delete: Удалить кошелёк - delete_conf: 'Вы уверены, что хотите удалить кошелек? Введите пароль для подтверждения:' + delete_conf: Вы уверены, что хотите удалить кошелек? delete_desc: Убедитесь, что вы сохранили вашу фразу восстановления, чтобы получить доступ к средствам в будущем. wallet_loading_err: 'Во время синхронизации кошелька произошла ошибка, вы можете повторить попытку или изменить настройки подключения, выбрав %{settings} внизу экрана.' wallet: Кошелёк diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index 3cb7a44..8b74d2b 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -14,7 +14,6 @@ use egui::{Align, Align2, Layout, Margin, RichText, Rounding, ScrollArea, TextStyle, Widget}; use egui_extras::{Size, StripBuilder}; -use grin_core::global::ChainTypes; use crate::AppConfig; use crate::gui::Colors; @@ -142,15 +141,11 @@ impl WalletsContent { self.wallets.add(wallet); }); } else { - let chain_type = AppConfig::chain_type(); - let list = if chain_type == ChainTypes::Mainnet { - &mut self.wallets.main_list - } else { - &mut self.wallets.test_list - }; - for wallet in list.iter_mut() { + let selected_id = self.wallets.selected_id.clone(); + let list = self.wallets.mut_list(); + for wallet in list { // Show content for selected wallet. - if self.wallets.selected_id == Some(wallet.config.id) { + if selected_id == Some(wallet.config.id) { // Setup wallet content width. let mut rect = ui.available_rect_before_wrap(); let mut width = ui.available_width(); @@ -348,7 +343,9 @@ impl WalletsContent { rect.set_width(width); ui.allocate_ui(rect.size(), |ui| { - let list = self.wallets.list().clone(); + let mut list = self.wallets.list().clone(); + // Remove deleted wallet from the list. + list.retain(|w| !w.is_deleted()); for wallet in &list { // Check if wallet reopen is needed. if !wallet.is_open() && wallet.reopen_needed() { @@ -479,7 +476,7 @@ impl WalletsContent { } else { let tx_progress = wallet.txs_sync_progress(); if tx_progress == 0 { - t!("wallets.tx_loading") + format!("{} {}", SPINNER, t!("wallets.tx_loading")) } else { format!("{} {}: {}%", SPINNER, diff --git a/src/gui/views/wallets/setup/common.rs b/src/gui/views/wallets/setup/common.rs index 8fd81f7..d24e0ee 100644 --- a/src/gui/views/wallets/setup/common.rs +++ b/src/gui/views/wallets/setup/common.rs @@ -71,7 +71,7 @@ impl CommonSetup { _: &mut eframe::Frame, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) { - // Draw modal content for this ui container. + // Show modal content for this ui container. self.modal_content_ui(ui, wallet, cb); ui.vertical_centered(|ui| { @@ -142,7 +142,7 @@ impl CommonSetup { }); } - /// Draw modal content for current ui container. + /// Draw [`Modal`] content for this ui container. fn modal_content_ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, diff --git a/src/gui/views/wallets/setup/connection.rs b/src/gui/views/wallets/setup/connection.rs index 3aa4ee3..33b9498 100644 --- a/src/gui/views/wallets/setup/connection.rs +++ b/src/gui/views/wallets/setup/connection.rs @@ -38,9 +38,6 @@ pub struct ConnectionSetup { /// Flag to show URL format error. ext_node_url_error: bool, - /// Flag to show closing progress at confirmation [`Modal`] to reopen the [`Wallet`]. - show_closing_progress: bool, - /// [`Modal`] identifiers allowed at this ui container. modal_ids: Vec<&'static str> } @@ -59,7 +56,6 @@ impl Default for ConnectionSetup { ext_node_url_edit: "".to_string(), ext_node_secret_edit: "".to_string(), ext_node_url_error: false, - show_closing_progress: false, modal_ids: vec![ ADD_EXT_CONNECTION_MODAL ] @@ -135,7 +131,11 @@ impl ConnectionSetup { if changed { wallet.config.save(); - self.show_reopen_confirmation_modal(); + // Show reopen confirmation modal. + Modal::new(REOPEN_WALLET_CONFIRMATION_MODAL) + .position(ModalPosition::Center) + .title(t!("modal.confirmation")) + .show(); } } @@ -423,66 +423,41 @@ impl ConnectionSetup { }); } - /// Show confirmation modal to reopen the [`Wallet`] after connection change. - fn show_reopen_confirmation_modal(&mut self,) { - self.show_closing_progress = false; - // Show modal. - Modal::new(REOPEN_WALLET_CONFIRMATION_MODAL) - .title(t!("modal.confirmation")) - .show(); - } - /// Draw confirmation modal content to reopen the [`Wallet`]. fn reopen_modal_content(&mut self, ui: &mut egui::Ui, wallet: &Wallet, modal: &Modal, cb: &dyn PlatformCallbacks) { - if self.show_closing_progress { - if !wallet.is_closing() { - modal.close(); - return; - } - ui.add_space(16.0); - ui.vertical_centered(|ui| { - View::small_loading_spinner(ui); - ui.add_space(12.0); - ui.label(RichText::new(t!("wallets.wallet_closing")) - .size(17.0) - .color(Colors::TEXT)); - }); - ui.add_space(10.0); - } else { - ui.add_space(8.0); - ui.vertical_centered(|ui| { - ui.label(RichText::new(t!("wallets.change_server_confirmation")) - .size(17.0) - .color(Colors::TEXT)); - }); - ui.add_space(10.0); + ui.add_space(8.0); + ui.vertical_centered(|ui| { + ui.label(RichText::new(t!("wallets.change_server_confirmation")) + .size(17.0) + .color(Colors::TEXT)); + }); + ui.add_space(10.0); - // Show modal buttons. - ui.scope(|ui| { - // Setup spacing between buttons. - ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); + // Show modal buttons. + ui.scope(|ui| { + // Setup spacing between buttons. + ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); - ui.columns(2, |columns| { - columns[0].vertical_centered_justified(|ui| { - View::button(ui, t!("modal.cancel"), Colors::WHITE, || { - modal.close(); - }); - }); - columns[1].vertical_centered_justified(|ui| { - View::button(ui, "OK".to_owned(), Colors::WHITE, || { - modal.disable_closing(); - self.show_closing_progress = true; - wallet.set_reopen(true); - wallet.close(); - }); + ui.columns(2, |columns| { + columns[0].vertical_centered_justified(|ui| { + View::button(ui, t!("modal.cancel"), Colors::WHITE, || { + modal.close(); + }); + }); + columns[1].vertical_centered_justified(|ui| { + View::button(ui, "OK".to_owned(), Colors::WHITE, || { + modal.disable_closing(); + wallet.set_reopen(true); + wallet.close(); + modal.close() }); }); - ui.add_space(6.0); }); - } + ui.add_space(6.0); + }); } } \ No newline at end of file diff --git a/src/gui/views/wallets/setup/recovery.rs b/src/gui/views/wallets/setup/recovery.rs index c163af6..73803fc 100644 --- a/src/gui/views/wallets/setup/recovery.rs +++ b/src/gui/views/wallets/setup/recovery.rs @@ -12,10 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::RichText; +use egui::{Align, Id, Layout, RichText, TextStyle, Widget}; use grin_chain::SyncStatus; +use grin_util::ZeroingString; + use crate::gui::Colors; -use crate::gui::icons::{EYE, STETHOSCOPE, TRASH, WRENCH}; +use crate::gui::icons::{EYE, EYE_SLASH, STETHOSCOPE, TRASH, WRENCH}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, View}; use crate::gui::views::types::ModalPosition; @@ -28,8 +30,11 @@ pub struct RecoverySetup { pass_edit: String, /// Flag to check if wrong password was entered. wrong_pass: bool, - /// Flag to show recovery phrase when password check was passed. - show_recovery_phrase: bool, + /// Flag to show/hide old password at [`egui::TextEdit`] field. + hide_pass: bool, + + /// Recovery phrase value. + recovery_phrase: Option, } /// Identifier for recovery phrase [`Modal`]. @@ -41,8 +46,9 @@ impl Default for RecoverySetup { fn default() -> Self { Self { wrong_pass: false, + hide_pass: false, pass_edit: "".to_string(), - show_recovery_phrase: false, + recovery_phrase: None, } } } @@ -53,7 +59,7 @@ impl RecoverySetup { _: &mut eframe::Frame, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) { - // Draw modal content for this ui container. + // Show modal content for this ui container. self.modal_content_ui(ui, wallet, cb); ui.add_space(10.0); @@ -66,17 +72,12 @@ impl RecoverySetup { ui.vertical_centered(|ui| { let integrated_node = wallet.get_current_ext_conn_id().is_none(); let integrated_node_ready = Node::get_sync_status() == Some(SyncStatus::NoSync); - if wallet.sync_error() || (integrated_node && !integrated_node_ready) { - ui.add_space(8.0); + ui.add_space(6.0); ui.label(RichText::new(t!("wallets.repair_unavailable")) .size(16.0) .color(Colors::RED)); - } else if wallet.is_repairing() { - ui.add_space(8.0); - View::small_loading_spinner(ui); - ui.add_space(1.0); - } else { + } else if !wallet.is_repairing() { ui.add_space(6.0); // Draw button to repair the wallet. let repair_text = format!("{} {}", STETHOSCOPE, t!("wallets.repair_wallet")); @@ -94,49 +95,35 @@ impl RecoverySetup { View::horizontal_line(ui, Colors::ITEM_STROKE); ui.add_space(6.0); - let recovery_phrase_text = format!("{}:", t!("wallets.recovery_phrase")); - ui.label(RichText::new(recovery_phrase_text).size(16.0).color(Colors::GRAY)); + let recovery_text = format!("{}:", t!("wallets.recovery_phrase")); + ui.label(RichText::new(recovery_text).size(16.0).color(Colors::GRAY)); ui.add_space(6.0); // Draw button to show recovery phrase. - let repair_text = format!("{} {}", EYE, t!("show")); - View::button(ui, repair_text, Colors::BUTTON, || { - // Setup modal values. - self.pass_edit = "".to_string(); - self.wrong_pass = false; - self.show_recovery_phrase = false; - // Show recovery phrase modal. - Modal::new(RECOVERY_PHRASE_MODAL) - .position(ModalPosition::CenterTop) - .title(t!("wallets.recovery_phrase")) - .show(); - cb.show_keyboard(); + let show_text = format!("{} {}", EYE, t!("show")); + View::button(ui, show_text, Colors::BUTTON, || { + self.show_recovery_phrase_modal(cb); }); ui.add_space(12.0); View::horizontal_line(ui, Colors::ITEM_STROKE); ui.add_space(6.0); - ui.label(RichText::new(t!("wallets.delete_desc")).size(16.0).color(Colors::GRAY)); + ui.label(RichText::new(t!("wallets.delete_desc")).size(16.0).color(Colors::TEXT)); ui.add_space(6.0); // Draw button to delete the wallet. let delete_text = format!("{} {}", TRASH, t!("wallets.delete")); View::button(ui, delete_text, Colors::GOLD, || { - // Setup modal values. - self.pass_edit = "".to_string(); - self.wrong_pass = false; - // Show wallet deletion confirmation modal. Modal::new(DELETE_CONFIRMATION_MODAL) - .position(ModalPosition::CenterTop) + .position(ModalPosition::Center) .title(t!("modal.confirmation")) .show(); - cb.show_keyboard(); }); ui.add_space(8.0); }); } - /// Draw modal content for current ui container. + /// Draw [`Modal`] content for this ui container. fn modal_content_ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, @@ -152,7 +139,7 @@ impl RecoverySetup { } DELETE_CONFIRMATION_MODAL => { Modal::ui(ui.ctx(), |ui, modal| { - self.delete_confirmation_modal_ui(ui, wallet, modal, cb); + self.deletion_modal_ui(ui, wallet, modal, cb); }); } _ => {} @@ -161,21 +148,157 @@ impl RecoverySetup { } } + /// Show recovery phrase [`Modal`]. + fn show_recovery_phrase_modal(&mut self, cb: &dyn PlatformCallbacks) { + // Setup modal values. + self.pass_edit = "".to_string(); + self.wrong_pass = false; + self.hide_pass = true; + self.recovery_phrase = None; + // Show recovery phrase modal. + Modal::new(RECOVERY_PHRASE_MODAL) + .position(ModalPosition::CenterTop) + .title(t!("wallets.recovery_phrase")) + .show(); + cb.show_keyboard(); + } + /// Draw recovery phrase [`Modal`] content. fn recovery_phrase_modal_ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, modal: &Modal, cb: &dyn PlatformCallbacks) { + ui.add_space(6.0); + if self.recovery_phrase.is_some() { + ui.vertical_centered(|ui| { + ui.label(RichText::new(self.recovery_phrase.clone().unwrap().to_string()) + .size(17.0) + .color(Colors::BLACK)); + }); + ui.add_space(6.0); + ui.vertical_centered_justified(|ui| { + View::button(ui, t!("close"), Colors::WHITE, || { + self.recovery_phrase = None; + modal.close(); + }); + }); + } else { + ui.vertical_centered(|ui| { + ui.label(RichText::new(t!("wallets.pass")) + .size(17.0) + .color(Colors::GRAY)); + ui.add_space(6.0); + }); + let mut rect = ui.available_rect_before_wrap(); + rect.set_height(34.0); + ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { + // Draw button to show/hide current password. + let eye_icon = if self.hide_pass { EYE } else { EYE_SLASH }; + View::button(ui, eye_icon.to_string(), Colors::WHITE, || { + self.hide_pass = !self.hide_pass; + }); + + let layout_size = ui.available_size(); + ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { + // Draw current wallet password text edit. + let pass_resp = egui::TextEdit::singleline(&mut self.pass_edit) + .id(Id::from(modal.id).with(wallet.config.id).with("recovery_phrase")) + .font(TextStyle::Heading) + .desired_width(ui.available_width()) + .cursor_at_end(true) + .password(self.hide_pass) + .ui(ui); + if pass_resp.clicked() { + cb.show_keyboard(); + } + pass_resp.request_focus(); + }); + }); + + // Show information when password is empty. + ui.vertical_centered(|ui| { + if self.pass_edit.is_empty() { + ui.add_space(8.0); + ui.label(RichText::new(t!("wallets.pass_empty")) + .size(17.0) + .color(Colors::INACTIVE_TEXT)); + } else if self.wrong_pass { + ui.add_space(8.0); + ui.label(RichText::new(t!("wallets.wrong_pass")) + .size(17.0) + .color(Colors::RED)); + } + ui.add_space(10.0); + }); + + // Show modal buttons. + ui.scope(|ui| { + // Setup spacing between buttons. + ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); + + ui.columns(2, |columns| { + columns[0].vertical_centered_justified(|ui| { + View::button(ui, t!("modal.cancel"), Colors::WHITE, || { + self.recovery_phrase = None; + modal.close(); + }); + }); + columns[1].vertical_centered_justified(|ui| { + View::button(ui, "OK".to_owned(), Colors::WHITE, || { + match wallet.get_recovery(self.pass_edit.clone()) { + Ok(phrase) => { + self.wrong_pass = false; + self.recovery_phrase = Some(phrase); + cb.hide_keyboard(); + } + Err(_) => { + self.wrong_pass = true; + } + } + }); + }); + }); + }); + } + ui.add_space(6.0); } - /// Draw recovery phrase [`Modal`] content. - fn delete_confirmation_modal_ui(&mut self, - ui: &mut egui::Ui, - wallet: &mut Wallet, - modal: &Modal, - cb: &dyn PlatformCallbacks) { + /// Draw wallet deletion [`Modal`] content. + fn deletion_modal_ui(&mut self, + ui: &mut egui::Ui, + wallet: &mut Wallet, + modal: &Modal, + cb: &dyn PlatformCallbacks) { + ui.add_space(8.0); + ui.vertical_centered(|ui| { + ui.label(RichText::new(t!("wallets.delete_conf")) + .size(17.0) + .color(Colors::TEXT)); + }); + ui.add_space(10.0); + // Show modal buttons. + ui.scope(|ui| { + // Setup spacing between buttons. + ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); + + ui.columns(2, |columns| { + columns[0].vertical_centered_justified(|ui| { + View::button(ui, t!("modal.cancel"), Colors::WHITE, || { + modal.close(); + }); + }); + columns[1].vertical_centered_justified(|ui| { + View::button(ui, t!("delete"), Colors::WHITE, || { + modal.disable_closing(); + wallet.set_reopen(true); + wallet.delete_wallet(); + }); + }); + }); + ui.add_space(6.0); + }); } } \ No newline at end of file diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index b018323..b748385 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -198,8 +198,8 @@ impl WalletContent { let integrated_node = wallet.get_current_ext_conn_id().is_none(); let integrated_node_ready = Node::get_sync_status() == Some(SyncStatus::NoSync); let sync_after_opening = wallet.get_data().is_none() && !wallet.sync_error(); - // Block navigation if wallet is repairing and integrated node is not launching, - // or wallet is closing or syncing after opening when there is no data to show. + // Block navigation if wallet is repairing and integrated node is not launching + // and if wallet is closing or syncing after opening when there is no data to show. (wallet.is_repairing() && (integrated_node_ready || !integrated_node) && !sync_error) || wallet.is_closing() || (sync_after_opening && !integrated_node) } diff --git a/src/wallet/list.rs b/src/wallet/list.rs index 395a9f2..322e5db 100644 --- a/src/wallet/list.rs +++ b/src/wallet/list.rs @@ -66,26 +66,40 @@ impl WalletList { /// Get [`Wallet`] list for current [`ChainTypes`]. pub fn list(&self) -> &Vec { - let chain_type = AppConfig::chain_type(); - if chain_type == ChainTypes::Mainnet { + if AppConfig::chain_type() == ChainTypes::Mainnet { &self.main_list } else { &self.test_list } } - /// Add created [`Wallet`] to the list. - pub fn add(&mut self, wallet: Wallet) { - self.selected_id = Some(wallet.config.id); - let chain_type = AppConfig::chain_type(); - let list = if chain_type == ChainTypes::Mainnet { + /// Get mutable [`Wallet`] list for current [`ChainTypes`]. + pub fn mut_list(&mut self) -> &mut Vec { + if AppConfig::chain_type() == ChainTypes::Mainnet { &mut self.main_list } else { &mut self.test_list - }; + } + } + + /// Add created [`Wallet`] to the list. + pub fn add(&mut self, wallet: Wallet) { + self.selected_id = Some(wallet.config.id); + let list = self.mut_list(); list.insert(0, wallet); } + /// Remove [`Wallet`] with provided identifier. + pub fn remove(&mut self, id: i64) { + let list = self.mut_list(); + for (index, wallet) in list.iter().enumerate() { + if wallet.config.id == id { + list.remove(index); + return; + } + } + } + /// Select [`Wallet`] with provided identifier. pub fn select(&mut self, id: Option) { self.selected_id = id; @@ -118,14 +132,9 @@ impl WalletList { /// Open selected [`Wallet`]. pub fn open_selected(&mut self, password: String) -> Result<(), Error> { - let chain_type = AppConfig::chain_type(); - let list = if chain_type == ChainTypes::Mainnet { - &mut self.main_list - } else { - &mut self.test_list - }; - for w in list { - if Some(w.config.id) == self.selected_id { + let selected_id = self.selected_id.clone(); + for w in self.mut_list() { + if Some(w.config.id) == selected_id { return w.open(password.clone()); } } diff --git a/src/wallet/wallet.rs b/src/wallet/wallet.rs index ec2946e..8cc2ab8 100644 --- a/src/wallet/wallet.rs +++ b/src/wallet/wallet.rs @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::{fs, thread}; use std::path::PathBuf; use std::sync::{Arc, mpsc, RwLock}; use std::sync::atomic::{AtomicBool, AtomicU8, Ordering}; -use std::thread; use std::thread::Thread; use std::time::Duration; @@ -53,6 +53,8 @@ pub struct Wallet { is_open: Arc, /// Flag to check if wallet is loading. closing: Arc, + /// Flag to check if wallet was deleted to remove it from list. + deleted: Arc, /// Error on wallet loading. sync_error: Arc, @@ -83,6 +85,7 @@ impl Wallet { reopen: Arc::new(AtomicBool::new(false)), is_open: Arc::from(AtomicBool::new(false)), closing: Arc::new(AtomicBool::new(false)), + deleted: Arc::new(AtomicBool::new(false)), sync_error: Arc::from(AtomicBool::new(false)), info_sync_progress: Arc::from(AtomicU8::new(0)), txs_sync_progress: Arc::from(AtomicU8::new(0)), @@ -183,8 +186,8 @@ impl Wallet { return Err(Error::GenericError("Already opened".to_string())); } - // Create new wallet instance if sync thread was stopped. - if self.sync_thread.write().unwrap().is_none() { + // Create new wallet instance if sync thread was stopped or instance was not created. + if self.sync_thread.write().unwrap().is_none() || self.instance.is_none() { let new_instance = Self::create_wallet_instance(self.config.clone())?; self.instance = Some(new_instance); self.instance_ext_conn_id = self.config.ext_conn_id; @@ -248,15 +251,11 @@ impl Wallet { self.closing.store(true, Ordering::Relaxed); // Close wallet at separate thread. - let mut wallet_close = self.clone(); + let wallet_close = self.clone(); let instance = wallet_close.instance.clone().unwrap(); thread::spawn(move || { // Close the wallet. - { - let mut wallet_lock = instance.lock(); - let lc = wallet_lock.lc_provider().unwrap(); - let _ = lc.close_wallet(None); - } + Self::close_wallet(&instance); // Mark wallet as not opened. wallet_close.closing.store(false, Ordering::Relaxed); @@ -270,6 +269,13 @@ impl Wallet { }); } + /// Close wallet for provided [`WalletInstance`]. + fn close_wallet(instance: &WalletInstance) { + let mut wallet_lock = instance.lock(); + let lc = wallet_lock.lc_provider().unwrap(); + let _ = lc.close_wallet(None); + } + /// Set wallet reopen status. pub fn set_reopen(&self, reopen: bool) { self.reopen.store(reopen, Ordering::Relaxed); @@ -356,8 +362,50 @@ impl Wallet { self.repair_progress.load(Ordering::Relaxed) } - pub fn delete_wallet(&self) { + /// Get recovery phrase. + pub fn get_recovery(&self, password: String) -> Result { + let instance = self.instance.clone().unwrap(); + let mut wallet_lock = instance.lock(); + let lc = wallet_lock.lc_provider().unwrap(); + lc.get_mnemonic(None, ZeroingString::from(password)) + } + /// Close the wallet, delete its files and mark it as deleted. + pub fn delete_wallet(&self) { + if !self.is_open() || self.instance.is_none() { + return; + } + self.closing.store(true, Ordering::Relaxed); + + // Delete wallet at separate thread. + let wallet_delete = self.clone(); + thread::spawn(move || { + thread::sleep(Duration::from_millis(1000)); + if let Some(instance) = wallet_delete.instance { + // Close the wallet. + Self::close_wallet(&instance); + // Remove wallet files. + let mut wallet_lock = instance.lock(); + let _ = wallet_lock.lc_provider().unwrap(); + let _ = fs::remove_dir_all(wallet_delete.config.get_data_path()); + } + + // Mark wallet as not opened and deleted. + wallet_delete.closing.store(false, Ordering::Relaxed); + wallet_delete.is_open.store(false, Ordering::Relaxed); + wallet_delete.deleted.store(true, Ordering::Relaxed); + + // Wake up wallet thread. + let thread_r = wallet_delete.sync_thread.read().unwrap(); + if let Some(thread) = thread_r.as_ref() { + thread.unpark(); + } + }); + } + + /// Check if wallet was deleted to remove it from list. + pub fn is_deleted(&self) -> bool { + self.deleted.load(Ordering::Relaxed) } }