messages: async response creation, better errors handling
This commit is contained in:
parent
d5fa21b6d7
commit
d6e6f89324
1 changed files with 127 additions and 102 deletions
|
@ -13,12 +13,10 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use egui::{Id, Margin, RichText, ScrollArea, TextBuffer};
|
use std::time::Duration;
|
||||||
use egui::os::OperatingSystem;
|
use egui::{Id, Margin, RichText, ScrollArea};
|
||||||
use egui::scroll_area::ScrollBarVisibility;
|
use egui::scroll_area::ScrollBarVisibility;
|
||||||
use egui::text_edit::TextEditState;
|
|
||||||
use grin_core::core::{amount_from_hr_string, amount_to_hr_string};
|
use grin_core::core::{amount_from_hr_string, amount_to_hr_string};
|
||||||
use grin_wallet_libwallet::{Slate, SlateState};
|
use grin_wallet_libwallet::{Slate, SlateState};
|
||||||
use log::error;
|
use log::error;
|
||||||
|
@ -29,7 +27,6 @@ use crate::gui::icons::{BROOM, CLIPBOARD_TEXT, COPY, DOWNLOAD_SIMPLE, PROHIBIT,
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{Modal, Root, View};
|
use crate::gui::views::{Modal, Root, View};
|
||||||
use crate::gui::views::types::{ModalPosition, TextEditOptions};
|
use crate::gui::views::types::{ModalPosition, TextEditOptions};
|
||||||
use crate::gui::views::views::LAST_SOFT_KEYBOARD_INPUT;
|
|
||||||
use crate::gui::views::wallets::wallet::types::{SLATEPACK_MESSAGE_HINT, WalletTab, WalletTabType};
|
use crate::gui::views::wallets::wallet::types::{SLATEPACK_MESSAGE_HINT, WalletTab, WalletTabType};
|
||||||
use crate::gui::views::wallets::wallet::WalletContent;
|
use crate::gui::views::wallets::wallet::WalletContent;
|
||||||
use crate::wallet::types::WalletTransaction;
|
use crate::wallet::types::WalletTransaction;
|
||||||
|
@ -64,6 +61,10 @@ pub struct WalletMessages {
|
||||||
message_edit: String,
|
message_edit: String,
|
||||||
/// Parsed Slatepack message.
|
/// Parsed Slatepack message.
|
||||||
message_slate: Option<Slate>,
|
message_slate: Option<Slate>,
|
||||||
|
/// Flag to check if message request is loading.
|
||||||
|
message_loading: bool,
|
||||||
|
/// Message request result.
|
||||||
|
receive_pay_result: Arc<RwLock<Option<(Slate, Result<String, grin_wallet_libwallet::Error>)>>>,
|
||||||
/// Slatepack error on finalization, parse and response creation.
|
/// Slatepack error on finalization, parse and response creation.
|
||||||
message_error: Option<MessageError>,
|
message_error: Option<MessageError>,
|
||||||
/// Generated Slatepack response message.
|
/// Generated Slatepack response message.
|
||||||
|
@ -80,7 +81,7 @@ pub struct WalletMessages {
|
||||||
/// Flag to check if there is an error happened on request creation at [`Modal`].
|
/// Flag to check if there is an error happened on request creation at [`Modal`].
|
||||||
request_error: Option<MessageError>,
|
request_error: Option<MessageError>,
|
||||||
/// Flag to check if request is loading at [`Modal`].
|
/// Flag to check if request is loading at [`Modal`].
|
||||||
request_loading: Arc<AtomicBool>,
|
request_loading: bool,
|
||||||
/// Request result if there is no error at [`Modal`].
|
/// Request result if there is no error at [`Modal`].
|
||||||
request_result: Arc<RwLock<Option<Result<(Slate, String), grin_wallet_libwallet::Error>>>>,
|
request_result: Arc<RwLock<Option<Result<(Slate, String), grin_wallet_libwallet::Error>>>>,
|
||||||
}
|
}
|
||||||
|
@ -141,13 +142,15 @@ impl WalletMessages {
|
||||||
send_request: false,
|
send_request: false,
|
||||||
message_edit: message.unwrap_or("".to_string()),
|
message_edit: message.unwrap_or("".to_string()),
|
||||||
message_slate: None,
|
message_slate: None,
|
||||||
|
message_loading: false,
|
||||||
|
receive_pay_result: Arc::new(RwLock::new(None)),
|
||||||
message_error: None,
|
message_error: None,
|
||||||
response_edit: "".to_string(),
|
response_edit: "".to_string(),
|
||||||
dandelion,
|
dandelion,
|
||||||
amount_edit: "".to_string(),
|
amount_edit: "".to_string(),
|
||||||
request_edit: "".to_string(),
|
request_edit: "".to_string(),
|
||||||
request_error: None,
|
request_error: None,
|
||||||
request_loading: Arc::new(AtomicBool::new(false)),
|
request_loading: false,
|
||||||
request_result: Arc::new(RwLock::new(None)),
|
request_result: Arc::new(RwLock::new(None)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,7 +220,7 @@ impl WalletMessages {
|
||||||
self.send_request = true;
|
self.send_request = true;
|
||||||
self.amount_edit = "".to_string();
|
self.amount_edit = "".to_string();
|
||||||
self.request_error = None;
|
self.request_error = None;
|
||||||
self.request_loading.store(false, Ordering::Relaxed);
|
self.request_loading = false;
|
||||||
{
|
{
|
||||||
let mut w_result = self.request_result.write();
|
let mut w_result = self.request_result.write();
|
||||||
*w_result = None;
|
*w_result = None;
|
||||||
|
@ -332,7 +335,7 @@ impl WalletMessages {
|
||||||
.id(input_id)
|
.id(input_id)
|
||||||
.font(egui::TextStyle::Small)
|
.font(egui::TextStyle::Small)
|
||||||
.desired_rows(5)
|
.desired_rows(5)
|
||||||
.interactive(response_empty)
|
.interactive(response_empty && !self.message_loading)
|
||||||
.hint_text(SLATEPACK_MESSAGE_HINT)
|
.hint_text(SLATEPACK_MESSAGE_HINT)
|
||||||
.desired_width(f32::INFINITY)
|
.desired_width(f32::INFINITY)
|
||||||
.show(ui)
|
.show(ui)
|
||||||
|
@ -341,40 +344,9 @@ impl WalletMessages {
|
||||||
if response_empty && resp.clicked() {
|
if response_empty && resp.clicked() {
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
}
|
}
|
||||||
if response_empty {
|
if response_empty && resp.has_focus() {
|
||||||
// Apply text from input on Android as temporary fix for egui.
|
// Apply text from input on Android as temporary fix for egui.
|
||||||
let os = OperatingSystem::from_target_os();
|
View::on_soft_input(ui, input_id, message);
|
||||||
if os == OperatingSystem::Android && resp.has_focus() {
|
|
||||||
let mut w_input = LAST_SOFT_KEYBOARD_INPUT.write();
|
|
||||||
|
|
||||||
if !w_input.is_empty() {
|
|
||||||
let mut state = TextEditState::load(ui.ctx(), input_id).unwrap();
|
|
||||||
match state.cursor.char_range() {
|
|
||||||
None => {}
|
|
||||||
Some(range) => {
|
|
||||||
let mut r = range.clone();
|
|
||||||
|
|
||||||
let mut index = r.primary.index;
|
|
||||||
|
|
||||||
message.insert_text(w_input.as_str(), index);
|
|
||||||
index = index + 1;
|
|
||||||
|
|
||||||
if index == 0 {
|
|
||||||
r.primary.index = message.len();
|
|
||||||
r.secondary.index = r.primary.index;
|
|
||||||
} else {
|
|
||||||
r.primary.index = index;
|
|
||||||
r.secondary.index = r.primary.index;
|
|
||||||
}
|
|
||||||
state.cursor.set_char_range(Some(r));
|
|
||||||
TextEditState::store(state, ui.ctx(), input_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*w_input = "".to_string();
|
|
||||||
ui.ctx().request_repaint();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
});
|
});
|
||||||
|
@ -389,7 +361,7 @@ impl WalletMessages {
|
||||||
|
|
||||||
// Draw buttons to clear/copy/paste.
|
// Draw buttons to clear/copy/paste.
|
||||||
let fields_empty = self.message_edit.is_empty() && self.response_edit.is_empty();
|
let fields_empty = self.message_edit.is_empty() && self.response_edit.is_empty();
|
||||||
let columns_num = if fields_empty { 1 } else { 2 };
|
let columns_num = if fields_empty || self.message_loading { 1 } else { 2 };
|
||||||
let mut show_dandelion = false;
|
let mut show_dandelion = false;
|
||||||
ui.scope(|ui| {
|
ui.scope(|ui| {
|
||||||
// Setup spacing between buttons.
|
// Setup spacing between buttons.
|
||||||
|
@ -419,16 +391,98 @@ impl WalletMessages {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let paste = format!("{} {}", CLIPBOARD_TEXT, t!("paste"));
|
if self.message_loading {
|
||||||
View::button(ui, paste, Colors::BUTTON, || {
|
View::small_loading_spinner(ui);
|
||||||
let buf = cb.get_string_from_buffer();
|
|
||||||
let previous = self.message_edit.clone();
|
// Check message loading result.
|
||||||
self.message_edit = buf.clone().trim().to_string();
|
let has_message_result = {
|
||||||
// Parse Slatepack message resetting message error.
|
let r_res = self.receive_pay_result.read();
|
||||||
if buf != previous {
|
r_res.is_some()
|
||||||
self.parse_message(wallet);
|
};
|
||||||
|
if has_message_result {
|
||||||
|
let (slate, resp) = {
|
||||||
|
let r_res = self.receive_pay_result.read();
|
||||||
|
r_res.as_ref().unwrap().clone()
|
||||||
|
};
|
||||||
|
if resp.is_ok() {
|
||||||
|
self.response_edit = resp.as_ref().unwrap().clone();
|
||||||
|
} else {
|
||||||
|
let err = resp.as_ref().err().unwrap();
|
||||||
|
match err {
|
||||||
|
// Set already canceled transaction error message.
|
||||||
|
grin_wallet_libwallet::Error::TransactionWasCancelled {..}
|
||||||
|
=> {
|
||||||
|
self.message_error = Some(
|
||||||
|
MessageError::Response(
|
||||||
|
t!("wallets.resp_canceled_err")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Set an error when there is not enough funds to pay.
|
||||||
|
grin_wallet_libwallet::Error::NotEnoughFunds {..} => {
|
||||||
|
let m = t!(
|
||||||
|
"wallets.pay_balance_error",
|
||||||
|
"amount" => amount_to_hr_string(slate.amount, true)
|
||||||
|
);
|
||||||
|
self.message_error = Some(MessageError::Response(m));
|
||||||
|
}
|
||||||
|
// Set default error message.
|
||||||
|
_ => {
|
||||||
|
self.message_error = Some(
|
||||||
|
MessageError::Response(
|
||||||
|
t!("wallets.resp_slatepack_err")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if tx with same slate id already exists.
|
||||||
|
if self.message_error.is_none() {
|
||||||
|
let exists_tx = wallet.tx_by_slate(&slate).is_some();
|
||||||
|
if exists_tx {
|
||||||
|
let mut sl = slate.clone();
|
||||||
|
sl.state = if sl.state == SlateState::Standard1 {
|
||||||
|
SlateState::Standard2
|
||||||
|
} else {
|
||||||
|
SlateState::Invoice2
|
||||||
|
};
|
||||||
|
match wallet.read_slatepack(&sl) {
|
||||||
|
None => {
|
||||||
|
self.message_error = Some(
|
||||||
|
MessageError::Response(
|
||||||
|
t!("wallets.resp_slatepack_err")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(sp) => {
|
||||||
|
self.response_edit = sp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Setup message slate.
|
||||||
|
if self.message_error.is_none() {
|
||||||
|
self.message_slate = Some(slate);
|
||||||
|
}
|
||||||
|
// Clear message loading result and status.
|
||||||
|
{
|
||||||
|
let mut w_res = self.receive_pay_result.write();
|
||||||
|
*w_res = None;
|
||||||
|
}
|
||||||
|
self.message_loading = false;
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
|
let paste = format!("{} {}", CLIPBOARD_TEXT, t!("paste"));
|
||||||
|
View::button(ui, paste, Colors::BUTTON, || {
|
||||||
|
let buf = cb.get_string_from_buffer();
|
||||||
|
let previous = self.message_edit.clone();
|
||||||
|
self.message_edit = buf.clone().trim().to_string();
|
||||||
|
// Parse Slatepack message resetting message error.
|
||||||
|
if buf != previous {
|
||||||
|
self.parse_message(wallet);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if columns_num == 1 {
|
if columns_num == 1 {
|
||||||
|
@ -546,52 +600,23 @@ impl WalletMessages {
|
||||||
// Make operation based on incoming state status.
|
// Make operation based on incoming state status.
|
||||||
match slate.state {
|
match slate.state {
|
||||||
SlateState::Standard1 | SlateState::Invoice1 => {
|
SlateState::Standard1 | SlateState::Invoice1 => {
|
||||||
let resp = if slate.state == SlateState::Standard1 {
|
let slate = slate.clone();
|
||||||
wallet.receive(&self.message_edit)
|
let message = self.message_edit.clone();
|
||||||
} else {
|
let message_result = self.receive_pay_result.clone();
|
||||||
wallet.pay(&self.message_edit)
|
let wallet = wallet.clone();
|
||||||
};
|
// Create response to sender or receiver at separate thread.
|
||||||
if resp.is_ok() {
|
self.message_loading = true;
|
||||||
self.response_edit = resp.unwrap();
|
thread::spawn(move || {
|
||||||
} else {
|
thread::sleep(Duration::from_millis(3000));
|
||||||
match resp.err().unwrap() {
|
let resp = if slate.state == SlateState::Standard1 {
|
||||||
grin_wallet_libwallet::Error::TransactionWasCancelled {..} => {
|
wallet.receive(&message)
|
||||||
// Set already canceled transaction error message.
|
} else {
|
||||||
self.message_error = Some(
|
wallet.pay(&message)
|
||||||
MessageError::Response(t!("wallets.resp_canceled_err"))
|
};
|
||||||
);
|
let mut w_res = message_result.write();
|
||||||
return;
|
*w_res = Some((slate, resp));
|
||||||
}
|
});
|
||||||
_ => {}
|
return;
|
||||||
}
|
|
||||||
// Check if tx with same slate id already exists.
|
|
||||||
let exists_tx = wallet.tx_by_slate(&slate).is_some();
|
|
||||||
if exists_tx {
|
|
||||||
let mut sl = slate.clone();
|
|
||||||
sl.state = if sl.state == SlateState::Standard1 {
|
|
||||||
SlateState::Standard2
|
|
||||||
} else {
|
|
||||||
SlateState::Invoice2
|
|
||||||
};
|
|
||||||
match wallet.read_slatepack(&sl) {
|
|
||||||
None => {
|
|
||||||
self.message_error = Some(
|
|
||||||
MessageError::Response(t!("wallets.resp_slatepack_err"))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Some(sp) => {
|
|
||||||
self.message_slate = Some(slate);
|
|
||||||
self.response_edit = sp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set default response error message.
|
|
||||||
self.message_error = Some(
|
|
||||||
MessageError::Response(t!("wallets.resp_slatepack_err"))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SlateState::Standard2 | SlateState::Invoice2 => {
|
SlateState::Standard2 | SlateState::Invoice2 => {
|
||||||
// Check if slatepack with same id and state already exists.
|
// Check if slatepack with same id and state already exists.
|
||||||
|
@ -640,7 +665,7 @@ impl WalletMessages {
|
||||||
modal: &Modal,
|
modal: &Modal,
|
||||||
cb: &dyn PlatformCallbacks) {
|
cb: &dyn PlatformCallbacks) {
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
if self.request_loading.load(Ordering::Relaxed) {
|
if self.request_loading {
|
||||||
ui.add_space(42.0);
|
ui.add_space(42.0);
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
View::big_loading_spinner(ui);
|
View::big_loading_spinner(ui);
|
||||||
|
@ -649,7 +674,7 @@ impl WalletMessages {
|
||||||
|
|
||||||
// Check if there is request result error.
|
// Check if there is request result error.
|
||||||
if self.request_error.is_some() {
|
if self.request_error.is_some() {
|
||||||
self.request_loading.store(false, Ordering::Relaxed);
|
self.request_loading = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -677,7 +702,7 @@ impl WalletMessages {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.request_loading.store(false, Ordering::Relaxed);
|
self.request_loading = false;
|
||||||
}
|
}
|
||||||
} else if self.request_edit.is_empty() {
|
} else if self.request_edit.is_empty() {
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
|
@ -775,7 +800,7 @@ impl WalletMessages {
|
||||||
let result = self.request_result.clone();
|
let result = self.request_result.clone();
|
||||||
|
|
||||||
// Send request at another thread.
|
// Send request at another thread.
|
||||||
self.request_loading.store(true, Ordering::Relaxed);
|
self.request_loading = true;
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let message = if send_request {
|
let message = if send_request {
|
||||||
wallet.send(a)
|
wallet.send(a)
|
||||||
|
|
Loading…
Reference in a new issue