txs: async tasks for wallet
This commit is contained in:
parent
b540fcbf19
commit
cd0e3485c5
19 changed files with 986 additions and 983 deletions
|
@ -86,6 +86,7 @@ wallets:
|
|||
tx_canceled: Abgebrochen
|
||||
tx_cancelling: Abbrechen
|
||||
tx_finalizing: Finalisierung
|
||||
tx_posting: Buchungsvorgang
|
||||
tx_confirmed: Bestätigt
|
||||
txs: Transaktionen
|
||||
tx: Transaktion
|
||||
|
@ -141,7 +142,7 @@ transport:
|
|||
incorrect_addr_err: 'Eingegebene Addresse ist inkorrekt:'
|
||||
tor_send_error: Beim Senden über Tor ist ein Fehler aufgetreten. Stellen Sie sicher, dass der Empfänger online ist. Die Transaktion wurde abgebrochen.
|
||||
tor_autorun_desc: Gibt an, ob beim Öffnen des Wallets der Tor-Dienst gestartet werden soll, um Transaktionen synchron zu empfangen.
|
||||
tor_sending: 'Sende %{amount} ツ über Tor'
|
||||
tor_sending: Sende über Tor
|
||||
tor_settings: Tor Einstellungen
|
||||
bridges: Brücken
|
||||
bridges_desc: Richten Sie Brücken ein, um die Zensur des Tor-Netzwerks zu umgehen, wenn die normale Verbindung nicht funktioniert.
|
||||
|
|
|
@ -86,6 +86,7 @@ wallets:
|
|||
tx_canceled: Canceled
|
||||
tx_cancelling: Cancelling
|
||||
tx_finalizing: Finalizing
|
||||
tx_posting: Posting
|
||||
tx_confirmed: Confirmed
|
||||
txs: Transactions
|
||||
tx: Transaction
|
||||
|
@ -141,7 +142,7 @@ transport:
|
|||
incorrect_addr_err: 'Entered address is incorrect:'
|
||||
tor_send_error: An error occurred during sending over Tor, make sure receiver is online, transaction was canceled.
|
||||
tor_autorun_desc: Whether to launch Tor service on wallet opening to receive transactions synchronously.
|
||||
tor_sending: 'Sending %{amount} ツ over Tor'
|
||||
tor_sending: Sending over Tor
|
||||
tor_settings: Tor Settings
|
||||
bridges: Bridges
|
||||
bridges_desc: Setup bridges to bypass Tor network censorship if usual connection is not working.
|
||||
|
|
|
@ -86,6 +86,7 @@ wallets:
|
|||
tx_canceled: Annulé
|
||||
tx_cancelling: Annulation
|
||||
tx_finalizing: Finalisation
|
||||
tx_posting: Publication
|
||||
tx_confirmed: Confirmé
|
||||
txs: Transactions
|
||||
tx: Transaction
|
||||
|
@ -141,7 +142,7 @@ transport:
|
|||
incorrect_addr_err: 'Adresse entrée incorrecte:'
|
||||
tor_send_error: "Une erreur s'est produite lors de l'envoi via Tor. Assurez-vous que le destinataire est en ligne, la transaction a été annulée."
|
||||
tor_autorun_desc: "Lancer automatiquement le service Tor à l'ouverture du portefeuille pour recevoir les transactions de manière synchronisée."
|
||||
tor_sending: 'Envoi de %{amount} ツ via Tor'
|
||||
tor_sending: Envoi via Tor
|
||||
tor_settings: Paramètres Tor
|
||||
bridges: Passerelles
|
||||
bridges_desc: Configurez des passerelles pour contourner la censure du réseau Tor si la connexion habituelle ne fonctionne pas.
|
||||
|
|
|
@ -86,6 +86,7 @@ wallets:
|
|||
tx_canceled: Отменено
|
||||
tx_cancelling: Отмена
|
||||
tx_finalizing: Завершение
|
||||
tx_posting: Публикация
|
||||
tx_confirmed: Подтверждено
|
||||
txs: Транзакции
|
||||
tx: Транзакция
|
||||
|
@ -141,7 +142,7 @@ transport:
|
|||
incorrect_addr_err: 'Введённый адрес неверен:'
|
||||
tor_send_error: Во время отправки через Tor произошла ошибка, убедитесь, что получатель находится онлайн, транзакция была отменена.
|
||||
tor_autorun_desc: Запускать ли Tor сервис при открытии кошелька для синхронного получения транзакций.
|
||||
tor_sending: 'Отправка %{amount} ツ через Tor'
|
||||
tor_sending: Отправка через Tor
|
||||
tor_settings: Настройки Tor
|
||||
bridges: Мосты
|
||||
bridges_desc: Настройте мосты для обхода цензуры сети Tor, если обычное соединение не работает.
|
||||
|
|
|
@ -86,6 +86,7 @@ wallets:
|
|||
tx_canceled: Iptal edildi
|
||||
tx_cancelling: Iptal ediliyor
|
||||
tx_finalizing: Islem tamamlaniyor
|
||||
tx_posting: Islem kaydetme
|
||||
tx_confirmed: Onaylandi
|
||||
txs: Islemler
|
||||
tx: Islem
|
||||
|
@ -141,7 +142,7 @@ transport:
|
|||
incorrect_addr_err: 'Girilen adres hatali:'
|
||||
tor_send_error: Tor adresi uzerinden gonderimde aksaklik olustu, alici online olmasi gerek, islem iptal edildi.
|
||||
tor_autorun_desc: Islemleri Tor adresi olarak AL,bunun için cuzdan acilisinda Tor hizmetinin baslatilip baslatilmayacagi.
|
||||
tor_sending: 'Tor adrese %{amount} ツ gonderiliyor.'
|
||||
tor_sending: Tor adrese gonderiliyor
|
||||
tor_settings: Tor Ayarlar
|
||||
bridges: Bridges
|
||||
bridges_desc: Setup bridges to bypass Tor network censorship if usual connection is not working.
|
||||
|
|
|
@ -81,11 +81,13 @@ wallets:
|
|||
tx_sent: 已发送
|
||||
tx_received: 已接收
|
||||
tx_sending: 发送中
|
||||
tx_sending_tor: 通过 Tor 发送
|
||||
tx_receiving: 接收中
|
||||
tx_confirming: 等待确认
|
||||
tx_canceled: 已取消
|
||||
tx_cancelling: 取消
|
||||
tx_finalizing: 完成
|
||||
tx_posting: 过账交易
|
||||
tx_confirmed: 已确认
|
||||
txs: 所有交易
|
||||
tx: 交易
|
||||
|
@ -141,7 +143,6 @@ transport:
|
|||
incorrect_addr_err: '输入的地址不正确:'
|
||||
tor_send_error: 通过 Tor 发送时出错,请确保接收方在线, 交易已取消.
|
||||
tor_autorun_desc: 是否在开钱包时启动 Tor 服务以同步接收交易.
|
||||
tor_sending: '通过 Tor 发送%{amount} ツ'
|
||||
tor_settings: Tor 设置
|
||||
bridges: 桥梁
|
||||
bridges_desc: 如果常规连接不正常,设置网桥,可以绕过 Tor 网络审查.
|
||||
|
|
|
@ -468,9 +468,12 @@ impl View {
|
|||
Spinner::new().size(Self::BIG_SPINNER_SIZE).color(Colors::gold()).ui(ui);
|
||||
}
|
||||
|
||||
/// Size of big loading spinner.
|
||||
pub const SMALL_SPINNER_SIZE: f32 = 32.0;
|
||||
|
||||
/// Draw small gold loading spinner.
|
||||
pub fn small_loading_spinner(ui: &mut egui::Ui) {
|
||||
Spinner::new().size(38.0).color(Colors::gold()).ui(ui);
|
||||
Spinner::new().size(30.0).color(Colors::gold()).ui(ui);
|
||||
}
|
||||
|
||||
/// Draw the button that looks like checkbox with callback on check.
|
||||
|
|
|
@ -26,7 +26,7 @@ use crate::gui::views::wallets::wallet::types::{wallet_status_text, WalletConten
|
|||
use crate::gui::views::wallets::WalletContent;
|
||||
use crate::gui::views::{Content, Modal, TitlePanel, View};
|
||||
use crate::gui::Colors;
|
||||
use crate::wallet::types::ConnectionMethod;
|
||||
use crate::wallet::types::{ConnectionMethod, WalletTask};
|
||||
use crate::wallet::{Wallet, WalletList};
|
||||
use crate::AppConfig;
|
||||
|
||||
|
@ -619,7 +619,7 @@ impl WalletsContent {
|
|||
fn select_wallet(&mut self, wallet: &Wallet, data: Option<String>, cb: &dyn PlatformCallbacks) {
|
||||
self.wallet_content.account_content.close_qr_scan(cb);
|
||||
if let Some(data) = data {
|
||||
wallet.open_message(data);
|
||||
wallet.task(WalletTask::OpenMessage(data));
|
||||
}
|
||||
self.wallets.select(Some(wallet.get_config().id));
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ use crate::gui::views::wallets::wallet::types::{WalletContentContainer, GRIN};
|
|||
use crate::gui::views::{CameraContent, CameraScanContent, Content, Modal, View};
|
||||
use crate::gui::Colors;
|
||||
use crate::wallet::{Wallet, WalletConfig};
|
||||
use crate::wallet::types::WalletTask;
|
||||
|
||||
/// Wallet account panel content.
|
||||
pub struct AccountContent {
|
||||
|
@ -288,7 +289,7 @@ impl AccountContent {
|
|||
//TODO: send with address
|
||||
}
|
||||
QrScanResult::Slatepack(m) => {
|
||||
wallet.open_message(m.to_string());
|
||||
wallet.task(WalletTask::OpenMessage(m));
|
||||
}
|
||||
_ => {
|
||||
self.qr_scan_result = Some(result);
|
||||
|
|
|
@ -16,7 +16,7 @@ use egui::scroll_area::ScrollBarVisibility;
|
|||
use egui::{Id, Margin, RichText, ScrollArea};
|
||||
use grin_chain::SyncStatus;
|
||||
use std::time::Duration;
|
||||
use grin_wallet_libwallet::Error;
|
||||
|
||||
use crate::gui::icons::{ARROWS_CLOCKWISE, FILE_ARROW_DOWN, FILE_ARROW_UP, GEAR_FINE, POWER, STACK};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::types::{LinePosition, ModalPosition};
|
||||
|
@ -30,7 +30,7 @@ use crate::gui::views::wallets::WalletTransactions;
|
|||
use crate::gui::views::{Content, FilePickContent, FilePickContentType, Modal, View};
|
||||
use crate::gui::Colors;
|
||||
use crate::node::Node;
|
||||
use crate::wallet::types::{ConnectionMethod, WalletTransaction};
|
||||
use crate::wallet::types::{ConnectionMethod, WalletTask};
|
||||
use crate::wallet::{ExternalConnection, Wallet};
|
||||
use crate::AppConfig;
|
||||
|
||||
|
@ -105,11 +105,6 @@ impl WalletContentContainer for WalletContent {
|
|||
show_account = false;
|
||||
}
|
||||
|
||||
// Consume inserted message.
|
||||
if let Some(res) = wallet.consume_message_result() {
|
||||
self.on_transaction(res);
|
||||
}
|
||||
|
||||
// Show wallet tabs.
|
||||
egui::TopBottomPanel::bottom("wallet_tabs")
|
||||
.frame(egui::Frame {
|
||||
|
@ -314,13 +309,6 @@ impl WalletContent {
|
|||
}
|
||||
}
|
||||
|
||||
/// Callback on incoming transaction for user to take action.
|
||||
fn on_transaction(&mut self, tx_result: Result<WalletTransaction, Error>) {
|
||||
if let Ok(tx) = tx_result {
|
||||
self.current_tab = Box::new(WalletTransactions::new(Some(tx)));
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if it's possible to go back at navigation stack.
|
||||
pub fn can_back(&self) -> bool {
|
||||
self.account_content.can_back() || self.transport_content.can_back()
|
||||
|
@ -370,41 +358,49 @@ impl WalletContent {
|
|||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
let active = if has_wallet_data {
|
||||
Some(false)
|
||||
if wallet.invoice_creating() {
|
||||
ui.add_space(4.0);
|
||||
View::small_loading_spinner(ui);
|
||||
} else {
|
||||
None
|
||||
};
|
||||
View::tab_button(ui, FILE_ARROW_DOWN, Some(Colors::green()), active, |_| {
|
||||
self.invoice_request_content = Some(InvoiceRequestContent::default());
|
||||
Modal::new(INVOICE_MODAL_ID)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("wallets.receive"))
|
||||
.show();
|
||||
});
|
||||
let active = if has_wallet_data {
|
||||
Some(false)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
View::tab_button(ui, FILE_ARROW_DOWN, Some(Colors::green()), active, |_| {
|
||||
self.invoice_request_content = Some(InvoiceRequestContent::default());
|
||||
Modal::new(INVOICE_MODAL_ID)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("wallets.receive"))
|
||||
.show();
|
||||
});
|
||||
}
|
||||
});
|
||||
columns[2].vertical_centered_justified(|ui| {
|
||||
if wallet.message_opening() {
|
||||
ui.add_space(4.0);
|
||||
View::small_loading_spinner(ui);
|
||||
} else {
|
||||
let mut message = "".to_string();
|
||||
self.file_pick_tab_button.ui(ui, cb, |m| {
|
||||
message = m;
|
||||
wallet.task(WalletTask::OpenMessage(m));
|
||||
});
|
||||
if !message.is_empty() {
|
||||
wallet.open_message(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
if can_send {
|
||||
columns[3].vertical_centered_justified(|ui| {
|
||||
View::tab_button(ui, FILE_ARROW_UP, Some(Colors::red()), Some(false), |_| {
|
||||
self.send_request_content = Some(SendRequestContent::new(None));
|
||||
Modal::new(SEND_MODAL_ID)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("wallets.send"))
|
||||
.show();
|
||||
});
|
||||
if wallet.send_creating() {
|
||||
ui.add_space(4.0);
|
||||
View::small_loading_spinner(ui);
|
||||
} else {
|
||||
let (icon, color) = (FILE_ARROW_UP, Some(Colors::red()));
|
||||
View::tab_button(ui, icon, color, Some(false), |_| {
|
||||
self.send_request_content = Some(SendRequestContent::new(None));
|
||||
Modal::new(SEND_MODAL_ID)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("wallets.send"))
|
||||
.show();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
let settings_index = if tabs_amount == 5 { 4 } else { 3 };
|
||||
|
|
|
@ -14,42 +14,23 @@
|
|||
|
||||
use egui::{Id, RichText};
|
||||
use grin_core::core::amount_from_hr_string;
|
||||
use grin_wallet_libwallet::Error;
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::wallets::wallet::WalletTransactionContent;
|
||||
use crate::gui::views::{Modal, TextEdit, View};
|
||||
use crate::gui::Colors;
|
||||
use crate::wallet::types::WalletTransaction;
|
||||
use crate::wallet::types::WalletTask;
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
/// Invoice request creation content.
|
||||
pub struct InvoiceRequestContent {
|
||||
/// Amount to receive.
|
||||
amount_edit: String,
|
||||
|
||||
/// Flag to check if request is loading.
|
||||
request_loading: bool,
|
||||
/// Request result if there is no error.
|
||||
request_result: Arc<RwLock<Option<Result<WalletTransaction, Error>>>>,
|
||||
/// Flag to check if there is an error happened on request creation.
|
||||
request_error: Option<String>,
|
||||
|
||||
/// Request result transaction content.
|
||||
result_tx_content: Option<WalletTransactionContent>,
|
||||
}
|
||||
|
||||
impl Default for InvoiceRequestContent {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
amount_edit: "".to_string(),
|
||||
request_loading: false,
|
||||
request_result: Arc::new(RwLock::new(None)),
|
||||
request_error: None,
|
||||
result_tx_content: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,42 +42,20 @@ impl InvoiceRequestContent {
|
|||
wallet: &Wallet,
|
||||
modal: &Modal,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
// Draw transaction information on request result.
|
||||
if let Some(tx) = self.result_tx_content.as_mut() {
|
||||
tx.ui(ui, wallet, modal, cb);
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup callback on continue.
|
||||
let on_continue = |m: &mut InvoiceRequestContent| {
|
||||
if m.amount_edit.is_empty() {
|
||||
return;
|
||||
}
|
||||
if let Ok(a) = amount_from_hr_string(m.amount_edit.as_str()) {
|
||||
modal.disable_closing();
|
||||
// Setup data for request.
|
||||
let wallet = wallet.clone();
|
||||
let result = m.request_result.clone();
|
||||
// Send request at another thread.
|
||||
m.request_loading = true;
|
||||
thread::spawn(move || {
|
||||
let res = wallet.issue_invoice(a);
|
||||
let mut w_result = result.write();
|
||||
*w_result = Some(res);
|
||||
});
|
||||
} else {
|
||||
m.request_error = Some(t!("wallets.invoice_slatepack_err"));
|
||||
m.amount_edit = "".to_string();
|
||||
wallet.task(WalletTask::Receive(a));
|
||||
Modal::close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Draw content on request loading.
|
||||
if self.request_loading {
|
||||
self.loading_request_ui(ui, modal);
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw amount input content.
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("wallets.enter_amount_receive"))
|
||||
|
@ -117,7 +76,6 @@ impl InvoiceRequestContent {
|
|||
|
||||
// Check value if input was changed.
|
||||
if amount_edit_before != self.amount_edit {
|
||||
self.request_error = None;
|
||||
if !self.amount_edit.is_empty() {
|
||||
self.amount_edit = self.amount_edit.trim().replace(",", ".");
|
||||
match amount_from_hr_string(self.amount_edit.as_str()) {
|
||||
|
@ -146,16 +104,6 @@ impl InvoiceRequestContent {
|
|||
}
|
||||
}
|
||||
|
||||
// Show request creation error.
|
||||
if let Some(err) = &self.request_error {
|
||||
ui.add_space(12.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(err)
|
||||
.size(17.0)
|
||||
.color(Colors::red()));
|
||||
});
|
||||
}
|
||||
|
||||
ui.add_space(12.0);
|
||||
|
||||
// Setup spacing between buttons.
|
||||
|
@ -165,7 +113,6 @@ impl InvoiceRequestContent {
|
|||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
self.amount_edit = "".to_string();
|
||||
self.request_error = None;
|
||||
Modal::close();
|
||||
});
|
||||
});
|
||||
|
@ -178,36 +125,4 @@ impl InvoiceRequestContent {
|
|||
});
|
||||
ui.add_space(6.0);
|
||||
}
|
||||
|
||||
/// Draw loading request content.
|
||||
fn loading_request_ui(&mut self, ui: &mut egui::Ui, modal: &Modal) {
|
||||
ui.add_space(34.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
View::big_loading_spinner(ui);
|
||||
});
|
||||
ui.add_space(50.0);
|
||||
|
||||
// Check if there is request result error.
|
||||
if self.request_error.is_some() {
|
||||
modal.enable_closing();
|
||||
self.request_loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Update data on request result.
|
||||
let r_request = self.request_result.read();
|
||||
if r_request.is_some() {
|
||||
modal.enable_closing();
|
||||
let result = r_request.as_ref().unwrap();
|
||||
match result {
|
||||
Ok(tx) => {
|
||||
self.result_tx_content = Some(WalletTransactionContent::new(tx));
|
||||
}
|
||||
Err(_) => {
|
||||
self.request_error = Some(t!("wallets.invoice_slatepack_err"));
|
||||
self.request_loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,19 +12,14 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use grin_core::core::{amount_from_hr_string, amount_to_hr_string};
|
||||
use grin_wallet_libwallet::{Error, SlatepackAddress};
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use egui::{Id, RichText};
|
||||
use grin_core::core::{amount_from_hr_string, amount_to_hr_string};
|
||||
use grin_wallet_libwallet::SlatepackAddress;
|
||||
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::wallets::wallet::WalletTransactionContent;
|
||||
use crate::gui::views::{CameraContent, Modal, TextEdit, View};
|
||||
use crate::gui::views::types::ModalPosition;
|
||||
use crate::wallet::types::WalletTransaction;
|
||||
use crate::gui::Colors;
|
||||
use crate::wallet::types::WalletTask;
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
/// Content to create a request to send funds.
|
||||
|
@ -36,18 +31,8 @@ pub struct SendRequestContent {
|
|||
/// Flag to check if entered address is incorrect.
|
||||
address_error: bool,
|
||||
|
||||
/// Flag to check if request is loading.
|
||||
request_loading: bool,
|
||||
/// Request result if there is no error.
|
||||
request_result: Arc<RwLock<Option<Result<WalletTransaction, Error>>>>,
|
||||
/// Flag to check if there is an error happened on request creation.
|
||||
request_error: Option<String>,
|
||||
|
||||
/// Address QR code scanner content.
|
||||
address_scan_content: Option<CameraContent>,
|
||||
|
||||
/// Request result transaction content.
|
||||
result_tx_content: Option<WalletTransactionContent>,
|
||||
}
|
||||
|
||||
impl SendRequestContent {
|
||||
|
@ -57,11 +42,7 @@ impl SendRequestContent {
|
|||
amount_edit: "".to_string(),
|
||||
address_edit: addr.unwrap_or("".to_string()),
|
||||
address_error: false,
|
||||
request_loading: false,
|
||||
request_result: Arc::new(RwLock::new(None)),
|
||||
request_error: None,
|
||||
address_scan_content: None,
|
||||
result_tx_content: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,68 +52,6 @@ impl SendRequestContent {
|
|||
wallet: &Wallet,
|
||||
modal: &Modal,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
// Draw transaction information on request result.
|
||||
if let Some(tx) = self.result_tx_content.as_mut() {
|
||||
tx.ui(ui, wallet, modal, cb);
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup callback on continue.
|
||||
let on_continue = |m: &mut SendRequestContent| {
|
||||
if m.amount_edit.is_empty() {
|
||||
return;
|
||||
}
|
||||
// Check address to send over Tor if enabled.
|
||||
let addr_str = m.address_edit.as_str();
|
||||
if let Ok(addr) = SlatepackAddress::try_from(addr_str.trim()) {
|
||||
if let Ok(a) = amount_from_hr_string(m.amount_edit.as_str()) {
|
||||
Modal::change_position(ModalPosition::Center);
|
||||
modal.disable_closing();
|
||||
|
||||
let mut wallet = wallet.clone();
|
||||
let res = m.request_result.clone();
|
||||
|
||||
// Send request at another thread.
|
||||
m.request_loading = true;
|
||||
thread::spawn(move || {
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(async {
|
||||
let result = wallet.send_tor(a, &addr).await;
|
||||
let mut w_res = res.write();
|
||||
*w_res = Some(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
} else if !addr_str.is_empty() {
|
||||
m.address_error = true;
|
||||
} else if let Ok(amount) = amount_from_hr_string(m.amount_edit.as_str()) {
|
||||
Modal::change_position(ModalPosition::Center);
|
||||
modal.disable_closing();
|
||||
|
||||
let wallet = wallet.clone();
|
||||
let result = m.request_result.clone();
|
||||
|
||||
// Send request at another thread.
|
||||
m.request_loading = true;
|
||||
thread::spawn(move || {
|
||||
let res = wallet.send(amount, None);
|
||||
let mut w_result = result.write();
|
||||
*w_result = Some(res);
|
||||
});
|
||||
} else {
|
||||
m.request_error = Some(t!("wallets.send_slatepack_err"));
|
||||
}
|
||||
};
|
||||
|
||||
// Draw content on request loading.
|
||||
if self.request_loading {
|
||||
self.loading_request_ui(ui, modal);
|
||||
return;
|
||||
}
|
||||
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Draw QR code scanner content if requested.
|
||||
|
@ -158,6 +77,7 @@ impl SendRequestContent {
|
|||
ui.columns(2, |cols| {
|
||||
cols[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("close"), Colors::white_or_black(false), || {
|
||||
on_stop();
|
||||
self.close();
|
||||
});
|
||||
});
|
||||
|
@ -267,17 +187,7 @@ impl SendRequestContent {
|
|||
|
||||
// Continue on Enter press.
|
||||
if address_edit.enter_pressed {
|
||||
on_continue(self);
|
||||
}
|
||||
|
||||
// Show request creation error.
|
||||
if let Some(err) = &self.request_error {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(err)
|
||||
.size(17.0)
|
||||
.color(Colors::red()));
|
||||
});
|
||||
ui.add_space(12.0);
|
||||
self.on_continue(wallet);
|
||||
}
|
||||
|
||||
// Setup spacing between buttons.
|
||||
|
@ -292,83 +202,38 @@ impl SendRequestContent {
|
|||
columns[1].vertical_centered_justified(|ui| {
|
||||
// Button to create Slatepack message request.
|
||||
View::button(ui, t!("continue"), Colors::white_or_black(false), || {
|
||||
on_continue(self);
|
||||
self.on_continue(wallet);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
}
|
||||
|
||||
/// Callback when Continue button was pressed.
|
||||
fn on_continue(&mut self, wallet: &Wallet) {
|
||||
if self.amount_edit.is_empty() {
|
||||
return;
|
||||
}
|
||||
// Check address to send over Tor if enabled.
|
||||
let addr_str = self.address_edit.as_str();
|
||||
if let Ok(r) = SlatepackAddress::try_from(addr_str.trim()) {
|
||||
if let Ok(a) = amount_from_hr_string(self.amount_edit.as_str()) {
|
||||
wallet.task(WalletTask::Send(a, Some(r)));
|
||||
Modal::close();
|
||||
}
|
||||
} else if !addr_str.is_empty() {
|
||||
self.address_error = true;
|
||||
} else if let Ok(a) = amount_from_hr_string(self.amount_edit.as_str()) {
|
||||
wallet.task(WalletTask::Send(a, None));
|
||||
Modal::close();
|
||||
}
|
||||
}
|
||||
|
||||
/// Close modal and clear data.
|
||||
fn close(&mut self) {
|
||||
self.amount_edit = "".to_string();
|
||||
self.address_edit = "".to_string();
|
||||
|
||||
let mut w_res = self.request_result.write();
|
||||
*w_res = None;
|
||||
|
||||
self.result_tx_content = None;
|
||||
self.address_scan_content = None;
|
||||
|
||||
Modal::close();
|
||||
}
|
||||
|
||||
/// Draw loading request content.
|
||||
fn loading_request_ui(&mut self, ui: &mut egui::Ui, modal: &Modal) {
|
||||
ui.add_space(40.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
View::big_loading_spinner(ui);
|
||||
});
|
||||
ui.add_space(40.0);
|
||||
|
||||
if !self.address_edit.is_empty() {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("transport.tor_sending", "amount" => self.amount_edit))
|
||||
.size(17.0)
|
||||
.color(Colors::inactive_text()));
|
||||
});
|
||||
ui.add_space(12.0);
|
||||
}
|
||||
|
||||
// Update data on request result.
|
||||
let has_res = {
|
||||
let r_request = self.request_result.read();
|
||||
r_request.is_some()
|
||||
};
|
||||
if has_res {
|
||||
self.request_loading = false;
|
||||
modal.enable_closing();
|
||||
let r_request = self.request_result.read();
|
||||
let result = r_request.as_ref().unwrap();
|
||||
match result {
|
||||
Ok(tx) => {
|
||||
self.result_tx_content = Some(WalletTransactionContent::new(tx));
|
||||
}
|
||||
Err(err) => {
|
||||
let m = match err {
|
||||
Error::NotEnoughFunds { .. } => {
|
||||
t!("wallets.pay_balance_error", "amount" => self.amount_edit)
|
||||
}
|
||||
_ => {
|
||||
if !self.address_edit.is_empty() {
|
||||
t!("transport.tor_send_error")
|
||||
} else {
|
||||
t!("wallets.send_slatepack_err")
|
||||
}
|
||||
}
|
||||
};
|
||||
self.request_error = Some(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there is request result error.
|
||||
if self.request_error.is_some() {
|
||||
Modal::change_position(ModalPosition::CenterTop);
|
||||
modal.enable_closing();
|
||||
let mut w_request = self.request_result.write();
|
||||
*w_request = None;
|
||||
self.request_loading = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -130,7 +130,7 @@ impl WalletTransportContent {
|
|||
if !Tor::is_service_running(service_id) {
|
||||
let r = CornerRadius::default();
|
||||
View::item_button(ui, r, POWER, Some(Colors::green()), || {
|
||||
if let Ok(key) = wallet.secret_key() {
|
||||
if let Ok(key) = wallet.get_secret_key() {
|
||||
let api_port = wallet.foreign_api_port().unwrap();
|
||||
Tor::start_service(api_port, key, service_id);
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ impl WalletTransportSettingsContent {
|
|||
// Restart running service or rebuild client.
|
||||
let service_id = &wallet.identifier();
|
||||
if Tor::is_service_running(service_id) {
|
||||
if let Ok(key) = wallet.secret_key() {
|
||||
if let Ok(key) = wallet.get_secret_key() {
|
||||
let api_port = wallet.foreign_api_port().unwrap();
|
||||
Tor::restart_service(api_port, key, service_id);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ use grin_wallet_libwallet::TxLogEntryType;
|
|||
use std::ops::Range;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use crate::gui::icons::{ARCHIVE_BOX, ARROW_CIRCLE_DOWN, ARROW_CIRCLE_UP, CALENDAR_CHECK, DOTS_THREE_CIRCLE, FILE_ARROW_DOWN, FILE_TEXT, GEAR_FINE, PROHIBIT, X_CIRCLE};
|
||||
use crate::gui::icons::{ARCHIVE_BOX, ARROW_CIRCLE_DOWN, ARROW_CIRCLE_UP, CALENDAR_CHECK, DOTS_THREE_CIRCLE, FILE_ARROW_DOWN, FILE_TEXT, GEAR_FINE, PROHIBIT, WARNING, X_CIRCLE};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::types::{LinePosition, ModalPosition};
|
||||
use crate::gui::views::wallets::types::WalletTab;
|
||||
|
@ -29,7 +29,7 @@ use crate::gui::views::wallets::wallet::types::{WalletTabType, GRIN};
|
|||
use crate::gui::views::wallets::wallet::WalletTransactionContent;
|
||||
use crate::gui::views::{Content, Modal, PullToRefresh, View};
|
||||
use crate::gui::Colors;
|
||||
use crate::wallet::types::{WalletData, WalletTransaction};
|
||||
use crate::wallet::types::{WalletData, WalletTask, WalletTransaction, WalletTransactionAction};
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
/// Wallet transactions tab content.
|
||||
|
@ -50,6 +50,15 @@ impl WalletTab for WalletTransactions {
|
|||
}
|
||||
|
||||
fn ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) {
|
||||
if Modal::opened().is_none() {
|
||||
// Show transaction modal on task result.
|
||||
if let Some(id) = wallet.consume_tx_task_result() {
|
||||
let tx = wallet.get_data().unwrap().tx_by_slate_id(id);
|
||||
if let Some(tx) = tx {
|
||||
self.show_tx_info_modal(tx.data.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.modal_content_ui(ui, wallet, cb);
|
||||
self.txs_ui(ui, wallet);
|
||||
}
|
||||
|
@ -72,7 +81,7 @@ impl WalletTransactions {
|
|||
manual_sync: None,
|
||||
};
|
||||
if let Some(tx) = &tx {
|
||||
content.show_tx_info_modal(tx);
|
||||
content.show_tx_info_modal(tx.data.id);
|
||||
}
|
||||
content
|
||||
}
|
||||
|
@ -104,7 +113,7 @@ impl WalletTransactions {
|
|||
return;
|
||||
}
|
||||
// Draw awaiting amount info if exists.
|
||||
self.awaiting_info_ui(ui, &data);
|
||||
self.awaiting_info_ui(ui, &data);
|
||||
});
|
||||
ui.add_space(4.0);
|
||||
|
||||
|
@ -161,10 +170,9 @@ impl WalletTransactions {
|
|||
r.nw = 0.0 as u8;
|
||||
r.sw = 0.0 as u8;
|
||||
View::item_button(ui, r, FILE_TEXT, None, || {
|
||||
self.show_tx_info_modal(tx);
|
||||
self.show_tx_info_modal(tx.data.id);
|
||||
});
|
||||
}
|
||||
|
||||
// Draw button to cancel transaction.
|
||||
if tx.can_cancel() {
|
||||
let (icon, color) = (PROHIBIT, Some(Colors::red()));
|
||||
|
@ -177,6 +185,10 @@ impl WalletTransactions {
|
|||
.show();
|
||||
});
|
||||
}
|
||||
//TODO: Draw button to repeat transaction task on error.
|
||||
if tx.action_error.is_some() {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -303,52 +315,68 @@ impl WalletTransactions {
|
|||
|| tx.data.tx_type == TxLogEntryType::TxReceivedCancelled;
|
||||
if is_canceled {
|
||||
format!("{} {}", X_CIRCLE, t!("wallets.tx_canceled"))
|
||||
} else if tx.finalizing {
|
||||
format!("{} {}", DOTS_THREE_CIRCLE, t!("wallets.tx_finalizing"))
|
||||
} else {
|
||||
if tx.cancelling {
|
||||
format!("{} {}", DOTS_THREE_CIRCLE, t!("wallets.tx_cancelling"))
|
||||
} else if let Some(a) = &tx.action {
|
||||
let error = if tx.action_error.is_none() {
|
||||
"".to_string()
|
||||
} else {
|
||||
match tx.data.tx_type {
|
||||
TxLogEntryType::TxReceived => {
|
||||
format!("{} {}",
|
||||
DOTS_THREE_CIRCLE,
|
||||
t!("wallets.tx_receiving"))
|
||||
},
|
||||
TxLogEntryType::TxSent => {
|
||||
format!("{} {}",
|
||||
DOTS_THREE_CIRCLE,
|
||||
t!("wallets.tx_sending"))
|
||||
},
|
||||
TxLogEntryType::ConfirmedCoinbase => {
|
||||
let tx_h = tx.height.unwrap_or(1) - 1;
|
||||
if tx_h != 0 {
|
||||
let left_conf = height - tx_h;
|
||||
if height >= tx_h && left_conf < COINBASE_MATURITY {
|
||||
let conf_info = format!("{}/{}",
|
||||
left_conf,
|
||||
COINBASE_MATURITY);
|
||||
format!("{} {} {}",
|
||||
DOTS_THREE_CIRCLE,
|
||||
t!("wallets.tx_confirming"),
|
||||
conf_info
|
||||
)
|
||||
} else {
|
||||
format!("{} {}",
|
||||
DOTS_THREE_CIRCLE,
|
||||
t!("wallets.tx_confirming"))
|
||||
}
|
||||
format!("{}: ", t!("error"))
|
||||
};
|
||||
let status = match a {
|
||||
WalletTransactionAction::Cancelling => t!("wallets.tx_cancelling"),
|
||||
WalletTransactionAction::Finalizing => t!("wallets.tx_finalizing"),
|
||||
WalletTransactionAction::Posting => t!("wallets.tx_posting"),
|
||||
WalletTransactionAction::SendingTor => t!("transport.tor_sending")
|
||||
};
|
||||
let icon = if error.is_empty() {
|
||||
DOTS_THREE_CIRCLE
|
||||
} else {
|
||||
WARNING
|
||||
};
|
||||
format!("{} {}{}", icon, error, status)
|
||||
} else {
|
||||
match tx.data.tx_type {
|
||||
TxLogEntryType::TxReceived => {
|
||||
let text = match tx.finalized() {
|
||||
true => t!("wallets.await_fin_amount"),
|
||||
false => t!("wallets.tx_receiving")
|
||||
};
|
||||
format!("{} {}", DOTS_THREE_CIRCLE, text)
|
||||
},
|
||||
TxLogEntryType::TxSent => {
|
||||
let text = match tx.finalized() {
|
||||
true => t!("wallets.await_fin_amount"),
|
||||
false => t!("wallets.tx_sending")
|
||||
};
|
||||
format!("{} {}", DOTS_THREE_CIRCLE, text)
|
||||
},
|
||||
TxLogEntryType::ConfirmedCoinbase => {
|
||||
let tx_h = tx.height.unwrap_or(1) - 1;
|
||||
if tx_h != 0 {
|
||||
let left_conf = height - tx_h;
|
||||
if height >= tx_h && left_conf < COINBASE_MATURITY {
|
||||
let conf_info = format!("{}/{}",
|
||||
left_conf,
|
||||
COINBASE_MATURITY);
|
||||
format!("{} {} {}",
|
||||
DOTS_THREE_CIRCLE,
|
||||
t!("wallets.tx_confirming"),
|
||||
conf_info
|
||||
)
|
||||
} else {
|
||||
format!("{} {}",
|
||||
DOTS_THREE_CIRCLE,
|
||||
t!("wallets.tx_confirming"))
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
} else {
|
||||
format!("{} {}",
|
||||
DOTS_THREE_CIRCLE,
|
||||
t!("wallets.tx_confirming"))
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
format!("{} {}",
|
||||
DOTS_THREE_CIRCLE,
|
||||
t!("wallets.tx_confirming"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -439,8 +467,8 @@ impl WalletTransactions {
|
|||
}
|
||||
|
||||
/// Show transaction information [`Modal`].
|
||||
fn show_tx_info_modal(&mut self, tx: &WalletTransaction) {
|
||||
let modal = WalletTransactionContent::new(tx);
|
||||
fn show_tx_info_modal(&mut self, id: u32) {
|
||||
let modal = WalletTransactionContent::new(id);
|
||||
self.tx_info_content = Some(modal);
|
||||
Modal::new(TX_INFO_MODAL)
|
||||
.position(ModalPosition::Center)
|
||||
|
@ -450,28 +478,29 @@ impl WalletTransactions {
|
|||
|
||||
/// Confirmation [`Modal`] to cancel transaction.
|
||||
fn cancel_confirmation_modal(&mut self, ui: &mut egui::Ui, wallet: &Wallet) {
|
||||
let data = wallet.get_data().unwrap();
|
||||
let data_txs = data.txs.unwrap();
|
||||
let txs = data_txs.into_iter()
|
||||
.filter(|tx| tx.data.id == self.confirm_cancel_tx_id.unwrap())
|
||||
.collect::<Vec<WalletTransaction>>();
|
||||
if txs.is_empty() {
|
||||
Modal::close();
|
||||
return;
|
||||
}
|
||||
let tx = txs.get(0).unwrap();
|
||||
let amount = amount_to_hr_string(tx.amount, true);
|
||||
let text = match tx.data.tx_type {
|
||||
TxLogEntryType::TxReceived => {
|
||||
t!("wallets.tx_receive_cancel_conf", "amount" => amount)
|
||||
},
|
||||
_ => {
|
||||
t!("wallets.tx_send_cancel_conf", "amount" => amount)
|
||||
}
|
||||
};
|
||||
|
||||
// Show confirmation text.
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
// Setup confirmation text.
|
||||
let data = wallet.get_data().unwrap();
|
||||
let data_txs = data.txs.unwrap();
|
||||
let txs = data_txs.into_iter()
|
||||
.filter(|tx| tx.data.id == self.confirm_cancel_tx_id.unwrap())
|
||||
.collect::<Vec<WalletTransaction>>();
|
||||
if txs.is_empty() {
|
||||
Modal::close();
|
||||
return;
|
||||
}
|
||||
let tx = txs.get(0).unwrap();
|
||||
let amount = amount_to_hr_string(tx.amount, true);
|
||||
let text = match tx.data.tx_type {
|
||||
TxLogEntryType::TxReceived => {
|
||||
t!("wallets.tx_receive_cancel_conf", "amount" => amount)
|
||||
},
|
||||
_ => {
|
||||
t!("wallets.tx_send_cancel_conf", "amount" => amount)
|
||||
}
|
||||
};
|
||||
ui.label(RichText::new(text)
|
||||
.size(17.0)
|
||||
.color(Colors::text(false)));
|
||||
|
@ -492,7 +521,7 @@ impl WalletTransactions {
|
|||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, "OK".to_string(), Colors::white_or_black(false), || {
|
||||
wallet.cancel(self.confirm_cancel_tx_id.unwrap());
|
||||
wallet.task(WalletTask::Cancel(tx.clone()));
|
||||
self.confirm_cancel_tx_id = None;
|
||||
Modal::close();
|
||||
});
|
||||
|
|
|
@ -17,12 +17,12 @@ use grin_core::core::amount_to_hr_string;
|
|||
use grin_util::ToHex;
|
||||
use grin_wallet_libwallet::TxLogEntryType;
|
||||
|
||||
use crate::gui::icons::{COPY, CUBE, FILE_ARCHIVE, FILE_TEXT, HASH_STRAIGHT, PROHIBIT, QR_CODE, SCAN};
|
||||
use crate::gui::icons::{CIRCLE_HALF, COPY, CUBE, FILE_ARCHIVE, FILE_TEXT, HASH_STRAIGHT, PROHIBIT, QR_CODE, SCAN};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::wallets::wallet::txs::WalletTransactions;
|
||||
use crate::gui::views::{CameraContent, FilePickContent, FilePickContentType, Modal, QrCodeContent, View};
|
||||
use crate::gui::Colors;
|
||||
use crate::wallet::types::WalletTransaction;
|
||||
use crate::wallet::types::{WalletTask, WalletTransaction};
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
/// Transaction information [`Modal`] content.
|
||||
|
@ -42,9 +42,9 @@ pub struct WalletTransactionContent {
|
|||
|
||||
impl WalletTransactionContent {
|
||||
/// Create new content instance with [`Wallet`] from provided [`WalletTransaction`].
|
||||
pub fn new(tx: &WalletTransaction) -> Self {
|
||||
pub fn new(id: u32) -> Self {
|
||||
Self {
|
||||
tx_id: tx.data.id,
|
||||
tx_id: id,
|
||||
qr_code_content: None,
|
||||
scan_qr_content: None,
|
||||
file_pick_button: FilePickContent::new(
|
||||
|
@ -103,7 +103,7 @@ impl WalletTransactionContent {
|
|||
modal.enable_closing();
|
||||
self.scan_qr_content = None;
|
||||
// Provide scan result as Slatepack message.
|
||||
wallet.open_message(result.text());
|
||||
wallet.task(WalletTask::OpenMessage(result.text()));
|
||||
} else {
|
||||
scan_content.ui(ui, cb);
|
||||
}
|
||||
|
@ -133,8 +133,8 @@ impl WalletTransactionContent {
|
|||
// Show transaction information.
|
||||
self.info_ui(ui, modal, tx, wallet, cb);
|
||||
|
||||
// Show transaction sharing content.
|
||||
if tx.can_finalize || tx.is_response {
|
||||
// Show transaction sharing content when can cancel or finalized.
|
||||
if tx.can_cancel() && !tx.finalized() {
|
||||
self.share_ui(ui, wallet, tx, cb);
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,7 @@ impl WalletTransactionContent {
|
|||
tx: &WalletTransaction,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
let amount = amount_to_hr_string(tx.amount, true);
|
||||
let desc_text = if tx.can_finalize {
|
||||
let desc_text = if tx.can_finalize() {
|
||||
if tx.data.tx_type == TxLogEntryType::TxSent {
|
||||
t!("wallets.send_request_desc", "amount" => amount)
|
||||
} else {
|
||||
|
@ -186,8 +186,8 @@ impl WalletTransactionContent {
|
|||
// Draw button to show Slatepack message as QR code.
|
||||
let qr_text = format!("{} {}", QR_CODE, t!("qr_code"));
|
||||
View::button(ui, qr_text.clone(), Colors::white_or_black(false), || {
|
||||
if let Some((_, d)) = wallet.read_slatepack_by_tx(tx) {
|
||||
self.qr_code_content = Some(QrCodeContent::new(d, true));
|
||||
if let Some(c) = wallet.read_slatepack(tx) {
|
||||
self.qr_code_content = Some(QrCodeContent::new(c, true));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -198,10 +198,12 @@ impl WalletTransactionContent {
|
|||
share_text,
|
||||
Colors::blue(),
|
||||
Colors::white_or_black(false), || {
|
||||
if let Some((s, d)) = wallet.read_slatepack_by_tx(tx) {
|
||||
let name = format!("{}.{}.slatepack", s.id, s.state);
|
||||
let data = d.as_bytes().to_vec();
|
||||
cb.share_data(name, data).unwrap_or_default();
|
||||
if let Some(slate_id) = tx.data.tx_slate_id {
|
||||
let name = format!("{}.{}.slatepack", slate_id, tx.state);
|
||||
if let Some(c) = wallet.read_slatepack(tx) {
|
||||
let data = c.as_bytes().to_vec();
|
||||
cb.share_data(name, data).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -242,10 +244,10 @@ impl WalletTransactionContent {
|
|||
}
|
||||
return;
|
||||
}
|
||||
if tx.can_finalize {
|
||||
if tx.can_finalize() {
|
||||
// Draw button to pick file.
|
||||
self.file_pick_button.ui(ui, cb, |data| {
|
||||
wallet.open_message(data);
|
||||
wallet.task(WalletTask::OpenMessage(data));
|
||||
});
|
||||
// Draw button to scan QR code.
|
||||
let r = CornerRadius::default();
|
||||
|
@ -257,13 +259,14 @@ impl WalletTransactionContent {
|
|||
}
|
||||
// Draw button to cancel transaction.
|
||||
if tx.can_cancel() {
|
||||
let r = if tx.can_finalize {
|
||||
let r = if tx.can_finalize() {
|
||||
CornerRadius::default()
|
||||
} else {
|
||||
View::item_rounding(0, 2, true)
|
||||
};
|
||||
View::item_button(ui, r, PROHIBIT, Some(Colors::red()), || {
|
||||
wallet.cancel(tx.data.id);
|
||||
wallet.task(WalletTask::Cancel(tx.clone()));
|
||||
Modal::close();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -280,7 +283,7 @@ impl WalletTransactionContent {
|
|||
}
|
||||
// Show receiver address.
|
||||
if let Some(rec) = tx.receiver() {
|
||||
let label = format!("{} {}", CUBE, t!("network_mining.address"));
|
||||
let label = format!("{} {}", CIRCLE_HALF, t!("network_mining.address"));
|
||||
info_item_ui(ui, rec.to_string(), label, true, cb);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ use serde_derive::{Deserialize, Serialize};
|
|||
|
||||
use crate::{AppConfig, Settings};
|
||||
use crate::wallet::ConnectionsConfig;
|
||||
use crate::wallet::types::ConnectionMethod;
|
||||
use crate::wallet::types::{ConnectionMethod, WalletTransaction};
|
||||
|
||||
/// Wallet configuration.
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
|
@ -192,15 +192,27 @@ impl WalletConfig {
|
|||
path.to_str().unwrap().to_string()
|
||||
}
|
||||
|
||||
/// Get Slatepacks data path for current wallet.
|
||||
pub fn get_slatepack_path(&self, slate: &Slate) -> PathBuf {
|
||||
/// Get Slatepack file path for transaction.
|
||||
pub fn get_tx_slate_path(&self, tx: &WalletTransaction) -> PathBuf {
|
||||
let mut path = PathBuf::from(self.get_wallet_path());
|
||||
path.push(SLATEPACKS_DIR_NAME);
|
||||
if !path.exists() {
|
||||
let _ = fs::create_dir_all(path.clone());
|
||||
}
|
||||
let slatepack_file_name = format!("{}.{}.slatepack", slate.id, slate.state);
|
||||
path.push(slatepack_file_name);
|
||||
let file = format!("{}.{}.slatepack", tx.data.tx_slate_id.unwrap(), tx.state);
|
||||
path.push(file);
|
||||
path
|
||||
}
|
||||
|
||||
/// Get Slatepack file path for Slate.
|
||||
pub fn get_slate_path(&self, slate: &Slate) -> PathBuf {
|
||||
let mut path = PathBuf::from(self.get_wallet_path());
|
||||
path.push(SLATEPACKS_DIR_NAME);
|
||||
if !path.exists() {
|
||||
let _ = fs::create_dir_all(path.clone());
|
||||
}
|
||||
let file = format!("{}.{}.slatepack", slate.id, slate.state);
|
||||
path.push(file);
|
||||
path
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ use std::sync::Arc;
|
|||
use grin_keychain::ExtKeychain;
|
||||
use grin_util::Mutex;
|
||||
use grin_wallet_impls::{DefaultLCProvider, HTTPNodeClient};
|
||||
use grin_wallet_libwallet::{SlatepackAddress, TxLogEntry, TxLogEntryType, WalletInfo, WalletInst};
|
||||
use grin_wallet_libwallet::{Error, Slate, SlateState, SlatepackAddress, TxLogEntry, TxLogEntryType, WalletInfo, WalletInst};
|
||||
use grin_wallet_util::OnionV3Address;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
|
@ -151,31 +151,133 @@ pub struct WalletData {
|
|||
pub txs: Option<Vec<WalletTransaction>>
|
||||
}
|
||||
|
||||
impl WalletData {
|
||||
/// Update transaction action status.
|
||||
pub fn on_tx_action(&mut self, id: String, action: Option<WalletTransactionAction>) {
|
||||
if self.txs.is_none() {
|
||||
return;
|
||||
}
|
||||
for tx in self.txs.as_mut().unwrap() {
|
||||
if let Some(slate_id) = tx.data.tx_slate_id {
|
||||
if slate_id.to_string() == id {
|
||||
tx.action = action;
|
||||
tx.action_error = None;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Update transaction action error status.
|
||||
pub fn on_tx_error(&mut self, id: String, err: Option<Error>) {
|
||||
if self.txs.is_none() {
|
||||
return;
|
||||
}
|
||||
for tx in self.txs.as_mut().unwrap() {
|
||||
if let Some(slate_id) = tx.data.tx_slate_id {
|
||||
if slate_id.to_string() == id {
|
||||
tx.action_error = err;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get transaction by slate identifier.
|
||||
pub fn tx_by_slate_id(&self, id: String) -> Option<WalletTransaction> {
|
||||
if self.txs.is_none() {
|
||||
return None;
|
||||
}
|
||||
for tx in self.txs.as_ref().unwrap() {
|
||||
if let Some(slate_id) = tx.data.tx_slate_id {
|
||||
if slate_id.to_string() == id {
|
||||
return Some(tx.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Wallet transaction action.
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum WalletTransactionAction {
|
||||
Cancelling, Finalizing, Posting, SendingTor
|
||||
}
|
||||
|
||||
/// Wallet transaction data.
|
||||
#[derive(Clone)]
|
||||
pub struct WalletTransaction {
|
||||
/// Transaction information.
|
||||
/// Information from database.
|
||||
pub data: TxLogEntry,
|
||||
/// State of transaction Slate.
|
||||
pub state: SlateState,
|
||||
/// Calculated transaction amount between debited and credited amount.
|
||||
pub amount: u64,
|
||||
/// Flag to check if transaction is cancelling.
|
||||
pub cancelling: bool,
|
||||
/// Flag to check if transaction can be sent back to initiator to finalize.
|
||||
pub is_response: bool,
|
||||
/// Flag to check if transaction can be finalized based on Slatepack message state.
|
||||
pub can_finalize: bool,
|
||||
/// Flag to check if transaction is finalizing.
|
||||
pub finalizing: bool,
|
||||
/// Block height where tx was included.
|
||||
pub height: Option<u64>,
|
||||
|
||||
/// Action on transaction.
|
||||
pub action: Option<WalletTransactionAction>,
|
||||
/// Action result error.
|
||||
pub action_error: Option<Error>
|
||||
}
|
||||
|
||||
impl WalletTransaction {
|
||||
/// Check if transactions can be finalized after receiving response.
|
||||
pub fn can_finalize(&self) -> bool {
|
||||
!self.cancelling() && !self.data.confirmed &&
|
||||
(!self.sending_tor() || self.action_error.is_some()) &&
|
||||
(self.data.tx_type == TxLogEntryType::TxSent ||
|
||||
self.data.tx_type == TxLogEntryType::TxReceived) &&
|
||||
(self.state == SlateState::Invoice1 || self.state == SlateState::Standard1)
|
||||
}
|
||||
|
||||
/// Check if transaction was finalized.
|
||||
pub fn finalized(&self) -> bool {
|
||||
(self.data.tx_type == TxLogEntryType::TxSent ||
|
||||
self.data.tx_type == TxLogEntryType::TxReceived) &&
|
||||
self.state == SlateState::Invoice3 || self.state == SlateState::Standard3
|
||||
}
|
||||
|
||||
/// Check if transaction is sending over Tor.
|
||||
pub fn sending_tor(&self) -> bool {
|
||||
if let Some(a) = self.action.as_ref() {
|
||||
return a == &WalletTransactionAction::SendingTor;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Check if transaction is cancelling.
|
||||
pub fn cancelling(&self) -> bool {
|
||||
if let Some(a) = self.action.as_ref() {
|
||||
return a == &WalletTransactionAction::Cancelling;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Check if transaction is posting.
|
||||
pub fn posting(&self) -> bool {
|
||||
if let Some(a) = self.action.as_ref() {
|
||||
return a == &WalletTransactionAction::Posting;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Check if transaction can be cancelled.
|
||||
pub fn can_cancel(&self) -> bool {
|
||||
!self.cancelling && !self.data.confirmed &&
|
||||
self.data.tx_type != TxLogEntryType::TxReceivedCancelled
|
||||
&& self.data.tx_type != TxLogEntryType::TxSentCancelled
|
||||
!self.cancelling() && !self.data.confirmed &&
|
||||
(!self.sending_tor() || self.action_error.is_some()) &&
|
||||
self.data.tx_type != TxLogEntryType::TxReceivedCancelled &&
|
||||
self.data.tx_type != TxLogEntryType::TxSentCancelled
|
||||
}
|
||||
|
||||
/// Check if transaction is finalizing.
|
||||
pub fn finalizing(&self) -> bool {
|
||||
if let Some(a) = self.action.as_ref() {
|
||||
return a == &WalletTransactionAction::Finalizing;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Get receiver address if payment proof was created.
|
||||
|
@ -188,4 +290,33 @@ impl WalletTransaction {
|
|||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Task for the wallet.
|
||||
#[derive(Clone)]
|
||||
pub enum WalletTask {
|
||||
/// Open Slatepack message parsing result and making an action.
|
||||
OpenMessage(String),
|
||||
/// Create request to send.
|
||||
/// * amount
|
||||
/// * receiver
|
||||
Send(u64, Option<SlatepackAddress>),
|
||||
/// Resend request over Tor.
|
||||
/// * local tx id
|
||||
/// * receiver
|
||||
SendTor(u32, SlatepackAddress),
|
||||
/// Invoice creation.
|
||||
/// * amount
|
||||
Receive(u64),
|
||||
/// Transaction finalization.
|
||||
/// * tx
|
||||
/// * local tx id
|
||||
Finalize(Option<Slate>, u32),
|
||||
/// Post transaction to blockchain.
|
||||
/// * tx
|
||||
/// * local tx id
|
||||
Post(Option<Slate>, u32),
|
||||
/// Cancel transaction.
|
||||
/// * tx
|
||||
Cancel(WalletTransaction),
|
||||
}
|
1204
src/wallet/wallet.rs
1204
src/wallet/wallet.rs
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue