mirror of
https://code.gri.mw/GUI/grim.git
synced 2026-07-18 04:38:55 +00:00
Payment proofs (#52)
- Ability to export and verify payment proofs Some fixes: - Migrated tx heights store from lmdb (also changed heights key from local id to slate_id to avoid conflict between wallets) - Close address panel on wallet change Reviewed-on: https://code.gri.mw/GUI/grim/pulls/52
This commit is contained in:
+1
-1
@@ -31,7 +31,7 @@ use crate::gui::Colors;
|
||||
/// QR code image from text.
|
||||
pub struct QrCodeContent {
|
||||
/// QR code text.
|
||||
text: String,
|
||||
pub text: String,
|
||||
|
||||
/// Maximum QR code size.
|
||||
max_size: f32,
|
||||
|
||||
@@ -31,6 +31,7 @@ use crate::node::Node;
|
||||
use crate::wallet::types::{ConnectionMethod, WalletTask};
|
||||
use crate::wallet::{ExternalConnection, Wallet};
|
||||
use crate::AppConfig;
|
||||
use crate::gui::views::wallets::wallet::proof::PaymentProofContent;
|
||||
|
||||
/// Wallet content.
|
||||
pub struct WalletContent {
|
||||
@@ -267,43 +268,7 @@ impl WalletContentContainer for WalletContent {
|
||||
}
|
||||
|
||||
// Handle wallet task result.
|
||||
if let Some((id, t)) = wallet.consume_task_result() {
|
||||
match Modal::opened() {
|
||||
None => {
|
||||
// Show transaction modal on wallet task result.
|
||||
if let Some(id) = id {
|
||||
let tx = wallet.get_data().unwrap().tx_by_slate_id(id);
|
||||
if tx.is_some() {
|
||||
self.txs_content = Some(WalletTransactionsContent::new(tx));
|
||||
self.settings_content = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(modal_id) => {
|
||||
match modal_id {
|
||||
SEND_MODAL_ID => {
|
||||
match t {
|
||||
WalletTask::CalculateFee(_, f) => {
|
||||
// Setup calculated tx fee at modal.
|
||||
if let Some(m) = self.send_content.as_mut() {
|
||||
if m.max_calculating {
|
||||
let data = wallet.get_data().unwrap();
|
||||
let a = data.info.amount_currently_spendable;
|
||||
let max = a - f;
|
||||
m.on_max_amount_calculated(max, f);
|
||||
} else {
|
||||
m.on_fee_calculated(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.handle_task_result(wallet);
|
||||
}
|
||||
let rect = {
|
||||
let mut r = rect.clone();
|
||||
@@ -466,6 +431,89 @@ impl WalletContent {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Handle wallet task result.
|
||||
fn handle_task_result(&mut self, wallet: &Wallet) {
|
||||
let res = wallet.consume_task_result();
|
||||
if res.is_none() {
|
||||
return;
|
||||
}
|
||||
let (id, t) = res.unwrap();
|
||||
match Modal::opened() {
|
||||
None => {
|
||||
// Show transaction modal on wallet task result.
|
||||
if let Some(id) = id {
|
||||
let tx = wallet.get_data().unwrap().tx_by_slate_id(id);
|
||||
if tx.is_some() {
|
||||
self.txs_content = Some(WalletTransactionsContent::new(tx));
|
||||
self.settings_content = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(modal_id) => {
|
||||
match modal_id {
|
||||
SEND_MODAL_ID => {
|
||||
match t {
|
||||
WalletTask::CalculateFee(_, f) => {
|
||||
// Setup calculated tx fee at modal.
|
||||
if let Some(m) = self.send_content.as_mut() {
|
||||
if m.max_calculating {
|
||||
let data = wallet.get_data().unwrap();
|
||||
let a = data.info.amount_currently_spendable;
|
||||
let max = if f > a {
|
||||
0
|
||||
} else {
|
||||
a - f
|
||||
};
|
||||
m.on_max_amount_calculated(max, f);
|
||||
} else {
|
||||
m.on_fee_calculated(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
MESSAGE_MODAL_ID => {
|
||||
match t {
|
||||
WalletTask::VerifyProof(proof, res) => {
|
||||
if let Some(res) = res {
|
||||
// Update message content on validation error
|
||||
// or when tx not belongs to current wallet.
|
||||
if res.is_err() || (!res.as_ref().unwrap().1 &&
|
||||
!res.as_ref().unwrap().2) {
|
||||
if let Some(m) = self.message_content.as_mut() {
|
||||
if let Ok(p) = serde_json::to_string_pretty(&proof) {
|
||||
let mut c = PaymentProofContent::new(Some(p));
|
||||
c.validation_result = Some(res);
|
||||
m.proof_content = Some(c);
|
||||
}
|
||||
}
|
||||
} else if let Ok((id, _, _)) = res {
|
||||
let tx = wallet.get_data().unwrap().tx_by_id(id);
|
||||
if let Some(tx) = tx {
|
||||
let mut tx_c = WalletTransactionsContent::new(Some(tx));
|
||||
if let Ok(p) = serde_json::to_string_pretty(&proof) {
|
||||
let proof_c = PaymentProofContent::new(Some(p));
|
||||
tx_c.tx_info_content
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.proof_content = Some(proof_c);
|
||||
self.txs_content = Some(tx_c);
|
||||
self.settings_content = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw content when wallet is syncing and not ready to use, returns `true` at this case.
|
||||
|
||||
@@ -15,10 +15,11 @@
|
||||
use egui::scroll_area::ScrollBarVisibility;
|
||||
use egui::{Id, RichText, ScrollArea};
|
||||
|
||||
use crate::gui::icons::{BROOM, CLIPBOARD_TEXT, SCAN};
|
||||
use crate::gui::icons::{BROOM, CLIPBOARD_TEXT, SCAN, SEAL_CHECK};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{CameraContent, FilePickContent, FilePickContentType, Modal, View};
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::views::wallets::wallet::proof::PaymentProofContent;
|
||||
use crate::wallet::types::WalletTask;
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
@@ -27,12 +28,13 @@ pub struct MessageInputContent {
|
||||
message_edit: String,
|
||||
/// Flag to check if error happened at Slatepack message parsing.
|
||||
parse_error: bool,
|
||||
|
||||
/// QR code scanner content.
|
||||
scan_qr_content: Option<CameraContent>,
|
||||
|
||||
/// Button to parse picked file content.
|
||||
file_pick_button: FilePickContent,
|
||||
|
||||
/// Payment proof input content.
|
||||
pub proof_content: Option<PaymentProofContent>,
|
||||
}
|
||||
|
||||
/// Hint for Slatepack message input.
|
||||
@@ -45,6 +47,7 @@ impl Default for MessageInputContent {
|
||||
parse_error: false,
|
||||
scan_qr_content: None,
|
||||
file_pick_button: FilePickContent::new(FilePickContentType::Button),
|
||||
proof_content: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,6 +91,19 @@ impl MessageInputContent {
|
||||
});
|
||||
});
|
||||
});
|
||||
} else if let Some(proof_content) = self.proof_content.as_mut() {
|
||||
proof_content.input_ui(ui, wallet, cb);
|
||||
ui.add_space(8.0);
|
||||
View::horizontal_line(ui, Colors::item_stroke());
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Show button to close modal.
|
||||
ui.vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("close"), Colors::white_or_black(false), || {
|
||||
self.message_edit = "".to_string();
|
||||
Modal::close();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
@@ -114,14 +130,18 @@ impl MessageInputContent {
|
||||
.show(ui, |ui| {
|
||||
ui.add_space(7.0);
|
||||
let input_id = scroll_id.with("_input");
|
||||
egui::TextEdit::multiline(&mut self.message_edit)
|
||||
let resp = egui::TextEdit::multiline(&mut self.message_edit)
|
||||
.id(input_id)
|
||||
.font(egui::TextStyle::Small)
|
||||
.desired_rows(5)
|
||||
.interactive(true)
|
||||
.hint_text(SLATEPACK_MESSAGE_HINT)
|
||||
.desired_width(f32::INFINITY)
|
||||
.show(ui).response;
|
||||
.show(ui)
|
||||
.response;
|
||||
if View::is_desktop() {
|
||||
resp.request_focus();
|
||||
}
|
||||
ui.add_space(6.0);
|
||||
});
|
||||
});
|
||||
@@ -181,6 +201,20 @@ impl MessageInputContent {
|
||||
View::horizontal_line(ui, Colors::item_stroke());
|
||||
ui.add_space(8.0);
|
||||
|
||||
ui.vertical_centered(|ui| {
|
||||
let proof_label = format!("{} {}", SEAL_CHECK, t!("wallets.payment_proof"));
|
||||
View::colored_text_button(ui,
|
||||
proof_label,
|
||||
Colors::gold_dark(),
|
||||
Colors::white_or_black(false), || {
|
||||
Modal::set_title(t!("wallets.payment_proof"));
|
||||
self.proof_content = Some(PaymentProofContent::new(None));
|
||||
});
|
||||
});
|
||||
ui.add_space(8.0);
|
||||
View::horizontal_line(ui, Colors::item_stroke());
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Show button to close modal.
|
||||
ui.vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("close"), Colors::white_or_black(false), || {
|
||||
|
||||
@@ -26,4 +26,5 @@ pub use content::WalletContent;
|
||||
mod account;
|
||||
mod transport;
|
||||
mod request;
|
||||
mod message;
|
||||
mod message;
|
||||
mod proof;
|
||||
@@ -0,0 +1,214 @@
|
||||
use egui::scroll_area::ScrollBarVisibility;
|
||||
use egui::{Id, RichText, ScrollArea};
|
||||
use grin_wallet_libwallet::{Error, PaymentProof};
|
||||
|
||||
use crate::gui::icons::{BROOM, CLIPBOARD_TEXT, COPY, FILE_TEXT, SEAL_CHECK};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{FilePickContent, FilePickContentType, Modal, View};
|
||||
use crate::gui::Colors;
|
||||
use crate::wallet::types::WalletTask;
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
pub struct PaymentProofContent {
|
||||
/// Payment proof text.
|
||||
input_edit: String,
|
||||
/// Button to pick payment proof file.
|
||||
pick_button: FilePickContent,
|
||||
/// Flag to check if an error occurred during proof parsing.
|
||||
parse_error: bool,
|
||||
/// Proof validation result.
|
||||
pub validation_result: Option<Result<(u32, bool, bool), Error>>,
|
||||
}
|
||||
|
||||
impl PaymentProofContent {
|
||||
/// Create new content to share or validate payment proof.
|
||||
pub fn new(proof_text: Option<String>) -> Self {
|
||||
Self {
|
||||
input_edit: proof_text.unwrap_or("".to_string()),
|
||||
pick_button: FilePickContent::new(FilePickContentType::Button),
|
||||
parse_error: false,
|
||||
validation_result: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw transaction payment proof input.
|
||||
pub fn input_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) {
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
if self.parse_error {
|
||||
let label_text = t!("wallets.payment_proof_error");
|
||||
ui.label(RichText::new(label_text).size(16.0).color(Colors::red()));
|
||||
} else if let Some(proof) = self.validation_result.as_ref() {
|
||||
match proof {
|
||||
Ok(_) => {
|
||||
let label_text = t!("wallets.payment_proof_valid");
|
||||
ui.label(RichText::new(label_text).size(16.0).color(Colors::green()));
|
||||
}
|
||||
Err(e) => {
|
||||
let error_text = t!("wallets.payment_proof_error");
|
||||
let label_text = format!("{} ({:?})", error_text, e);
|
||||
ui.label(RichText::new(label_text).size(16.0).color(Colors::red()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let desc_label = t!("wallets.payment_proof_desc");
|
||||
ui.label(RichText::new(desc_label).size(16.0).color(Colors::inactive_text()));
|
||||
}
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
let scroll_id = Id::from("tx_info_payment_proof_input");
|
||||
View::horizontal_line(ui, Colors::item_stroke());
|
||||
ui.add_space(3.0);
|
||||
ScrollArea::vertical()
|
||||
.id_salt(scroll_id)
|
||||
.scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden)
|
||||
.max_height(128.0)
|
||||
.auto_shrink([false; 2])
|
||||
.show(ui, |ui| {
|
||||
ui.add_space(7.0);
|
||||
let input_id = scroll_id.with("edit");
|
||||
let proof_input_before = self.input_edit.clone();
|
||||
let resp = egui::TextEdit::multiline(&mut self.input_edit)
|
||||
.id(input_id)
|
||||
.font(egui::TextStyle::Small)
|
||||
.desired_rows(5)
|
||||
.interactive(!wallet.payment_proof_verifying())
|
||||
.desired_width(f32::INFINITY)
|
||||
.show(ui)
|
||||
.response;
|
||||
if View::is_desktop() {
|
||||
resp.request_focus();
|
||||
}
|
||||
// Parse payment proof on input change.
|
||||
if self.input_edit != proof_input_before {
|
||||
self.on_proof_edit_change(wallet);
|
||||
}
|
||||
ui.add_space(6.0);
|
||||
});
|
||||
});
|
||||
|
||||
ui.add_space(2.0);
|
||||
View::horizontal_line(ui, Colors::item_stroke());
|
||||
ui.add_space(8.0);
|
||||
|
||||
if wallet.payment_proof_verifying() {
|
||||
ui.vertical_centered(|ui| {
|
||||
View::small_loading_spinner(ui);
|
||||
});
|
||||
} else {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
if self.parse_error || (self.validation_result.is_some() &&
|
||||
self.validation_result.as_ref().unwrap().is_err()) {
|
||||
// Draw button to clear message input.
|
||||
let clear_text = format!("{} {}", BROOM, t!("clear"));
|
||||
View::button(ui, clear_text, Colors::white_or_black(false), || {
|
||||
self.input_edit = "".to_string();
|
||||
self.parse_error = false;
|
||||
self.validation_result = None;
|
||||
});
|
||||
} else {
|
||||
// Draw button to paste proof text.
|
||||
let paste_text = format!("{} {}", CLIPBOARD_TEXT, t!("paste"));
|
||||
View::button(ui, paste_text, Colors::white_or_black(false), || {
|
||||
self.input_edit = cb.get_string_from_buffer();
|
||||
self.on_proof_edit_change(wallet);
|
||||
});
|
||||
}
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
let mut changed = false;
|
||||
self.pick_button.ui(ui, cb, |data| {
|
||||
self.input_edit = data.clone();
|
||||
changed = true;
|
||||
});
|
||||
if changed {
|
||||
self.on_proof_edit_change(wallet);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Callback on payment proof input change.
|
||||
fn on_proof_edit_change(&mut self, wallet: &Wallet) {
|
||||
if wallet.payment_proof_verifying() {
|
||||
return;
|
||||
}
|
||||
if self.input_edit.is_empty() {
|
||||
self.parse_error = false;
|
||||
return;
|
||||
}
|
||||
if let Ok(p) = serde_json::from_str::<PaymentProof>(self.input_edit.as_str()) {
|
||||
wallet.task(WalletTask::VerifyProof(p, None));
|
||||
} else {
|
||||
self.parse_error = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw transaction payment proof content to share.
|
||||
pub fn share_ui(&mut self, ui: &mut egui::Ui, file_name: String, cb: &dyn PlatformCallbacks) {
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
let desc = format!("{} {}:", SEAL_CHECK, t!("wallets.payment_proof"));
|
||||
ui.label(RichText::new(desc).size(16.0).color(Colors::inactive_text()));
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
let scroll_id = Id::from("tx_info_payment_proof_share");
|
||||
View::horizontal_line(ui, Colors::item_stroke());
|
||||
ui.add_space(3.0);
|
||||
ScrollArea::vertical()
|
||||
.id_salt(scroll_id)
|
||||
.scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden)
|
||||
.max_height(128.0)
|
||||
.auto_shrink([false; 2])
|
||||
.show(ui, |ui| {
|
||||
ui.add_space(7.0);
|
||||
let input_id = scroll_id.with("edit");
|
||||
egui::TextEdit::multiline(&mut self.input_edit)
|
||||
.id(input_id)
|
||||
.font(egui::TextStyle::Small)
|
||||
.desired_rows(5)
|
||||
.interactive(false)
|
||||
.desired_width(f32::INFINITY)
|
||||
.show(ui);
|
||||
ui.add_space(6.0);
|
||||
});
|
||||
});
|
||||
|
||||
ui.add_space(2.0);
|
||||
View::horizontal_line(ui, Colors::item_stroke());
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
// Draw copy button.
|
||||
let copy_text = format!("{} {}", COPY, t!("copy"));
|
||||
View::button(ui, copy_text, Colors::white_or_black(false), || {
|
||||
cb.copy_string_to_buffer(self.input_edit.clone());
|
||||
Modal::close();
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
let share_text = format!("{} {}", FILE_TEXT, t!("share"));
|
||||
View::colored_text_button(ui,
|
||||
share_text,
|
||||
Colors::blue(),
|
||||
Colors::white_or_black(false), || {
|
||||
let file_name = format!("{}.txt", file_name);
|
||||
let data = self.input_edit.as_bytes().to_vec();
|
||||
cb.share_data(file_name, data).unwrap_or_default();
|
||||
Modal::close();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -63,8 +63,13 @@ impl SendRequestContent {
|
||||
/// Setup maximum amount to send and fee.
|
||||
pub fn on_max_amount_calculated(&mut self, amount: u64, fee: u64) {
|
||||
self.max_calculating = false;
|
||||
self.amount_edit = amount_to_hr_string(amount, true);
|
||||
self.fee_edit = amount_to_hr_string(fee, true);
|
||||
if amount == 0 {
|
||||
self.amount_edit = "".to_string();
|
||||
self.fee_edit = "".to_string();
|
||||
} else {
|
||||
self.amount_edit = amount_to_hr_string(amount, true);
|
||||
self.fee_edit = amount_to_hr_string(fee, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw [`Modal`] content.
|
||||
|
||||
@@ -40,11 +40,16 @@ impl WalletContentContainer for WalletTransportContent {
|
||||
|
||||
fn container_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) {
|
||||
if let Some(content) = self.qr_address_content.as_mut() {
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Close panel on wallet change.
|
||||
if let Some(address) = wallet.slatepack_address() {
|
||||
if address != content.text {
|
||||
self.qr_address_content = None;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Draw QR code content.
|
||||
ui.add_space(6.0);
|
||||
content.ui(ui, cb);
|
||||
|
||||
ui.vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("close"), Colors::white_or_black(false), || {
|
||||
self.qr_address_content = None;
|
||||
|
||||
@@ -34,7 +34,7 @@ use crate::wallet::Wallet;
|
||||
/// Wallet transactions tab content.
|
||||
pub struct WalletTransactionsContent {
|
||||
/// Transaction information [`Modal`] content.
|
||||
tx_info_content: Option<WalletTransactionContent>,
|
||||
pub tx_info_content: Option<WalletTransactionContent>,
|
||||
|
||||
/// Transaction identifier to use at confirmation [`Modal`].
|
||||
confirm_cancel_tx_id: Option<u32>,
|
||||
@@ -69,6 +69,7 @@ impl WalletContentContainer for WalletTransactionsContent {
|
||||
|
||||
/// Identifier for transaction information [`Modal`].
|
||||
const TX_INFO_MODAL: &'static str = "tx_info_modal";
|
||||
|
||||
/// Identifier for transaction cancellation confirmation [`Modal`].
|
||||
const CANCEL_TX_CONFIRMATION_MODAL: &'static str = "cancel_tx_conf_modal";
|
||||
|
||||
@@ -171,7 +172,7 @@ impl WalletTransactionsContent {
|
||||
let tx = txs.get(index).unwrap();
|
||||
Self::tx_item_ui(ui, tx, rect, &data, |ui| {
|
||||
// Draw button to show transaction info.
|
||||
if tx.data.tx_slate_id.is_some() {
|
||||
if tx.data.tx_slate_id.is_some() || tx.data.payment_proof.is_some() {
|
||||
r.nw = 0.0 as u8;
|
||||
r.sw = 0.0 as u8;
|
||||
View::item_button(ui, r, FILE_TEXT, None, || {
|
||||
@@ -459,17 +460,17 @@ impl WalletTransactionsContent {
|
||||
let (icon, color) = (ARROWS_CLOCKWISE, Some(Colors::green()));
|
||||
View::item_button(ui, rounding, icon, color, || {
|
||||
if repost {
|
||||
wallet.task(WalletTask::Post(None, tx.data.id));
|
||||
wallet.task(WalletTask::Post(tx.data.id));
|
||||
} else {
|
||||
match tx.action.as_ref().unwrap() {
|
||||
WalletTransactionAction::Cancelling => {
|
||||
wallet.task(WalletTask::Cancel(tx.clone()));
|
||||
wallet.task(WalletTask::Cancel(tx.data.clone()));
|
||||
}
|
||||
WalletTransactionAction::Finalizing => {
|
||||
wallet.task(WalletTask::Finalize(None, tx.data.id));
|
||||
wallet.task(WalletTask::Finalize(tx.data.id));
|
||||
}
|
||||
WalletTransactionAction::Posting => {
|
||||
wallet.task(WalletTask::Post(None, tx.data.id));
|
||||
wallet.task(WalletTask::Post(tx.data.id));
|
||||
}
|
||||
WalletTransactionAction::SendingTor => {
|
||||
if let Some(a) = &tx.receiver {
|
||||
@@ -536,7 +537,7 @@ impl WalletTransactionsContent {
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, "OK".to_string(), Colors::white_or_black(false), || {
|
||||
wallet.task(WalletTask::Cancel(tx.clone()));
|
||||
wallet.task(WalletTask::Cancel(tx.data.clone()));
|
||||
self.confirm_cancel_tx_id = None;
|
||||
Modal::close();
|
||||
});
|
||||
|
||||
@@ -19,8 +19,9 @@ use grin_util::ToHex;
|
||||
use grin_wallet_libwallet::TxLogEntryType;
|
||||
use std::fs;
|
||||
|
||||
use crate::gui::icons::{CIRCLE_HALF, COPY, CUBE, FILE_ARCHIVE, FILE_TEXT, HASH_STRAIGHT, PROHIBIT, QR_CODE};
|
||||
use crate::gui::icons::{CIRCLE_HALF, COPY, CUBE, FILE_ARCHIVE, FILE_TEXT, HASH_STRAIGHT, PROHIBIT, QR_CODE, SEAL_CHECK};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::wallets::wallet::proof::PaymentProofContent;
|
||||
use crate::gui::views::wallets::wallet::txs::WalletTransactionsContent;
|
||||
use crate::gui::views::{Modal, QrCodeContent, View};
|
||||
use crate::gui::Colors;
|
||||
@@ -36,15 +37,19 @@ pub struct WalletTransactionContent {
|
||||
|
||||
/// QR code Slatepack message image content.
|
||||
qr_code_content: Option<QrCodeContent>,
|
||||
|
||||
/// Payment proof sharing content.
|
||||
pub proof_content: Option<PaymentProofContent>,
|
||||
}
|
||||
|
||||
impl WalletTransactionContent {
|
||||
/// Create new content instance with [`Wallet`] from provided [`WalletTransaction`].
|
||||
pub fn new(id: u32) -> Self {
|
||||
pub fn new(tx_id: u32) -> Self {
|
||||
Self {
|
||||
tx_id: id,
|
||||
tx_id,
|
||||
message: None,
|
||||
qr_code_content: None,
|
||||
proof_content: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,9 +97,35 @@ impl WalletTransactionContent {
|
||||
// Show transaction information.
|
||||
self.info_ui(ui, tx, wallet, cb);
|
||||
|
||||
// Show transaction sharing content when can cancel or finalized.
|
||||
if tx.can_cancel() && !tx.finalized() {
|
||||
// Show transaction sharing content or payment proof.
|
||||
if self.proof_content.is_none() && tx.can_cancel() && !tx.finalized() {
|
||||
self.share_ui(ui, wallet, tx, cb);
|
||||
} else {
|
||||
if let Some(proof_content) = self.proof_content.as_mut() {
|
||||
// Payment proof file name setup.
|
||||
let file_name = if let Some(slate_id) = tx.data.tx_slate_id {
|
||||
slate_id.to_string()
|
||||
} else {
|
||||
tx.data.id.to_string()
|
||||
};
|
||||
// Draw payment proof sharing content.
|
||||
proof_content.share_ui(ui, file_name, cb);
|
||||
} else if tx.proof.is_some() && !tx.sending_tor() &&
|
||||
tx.action_error.is_none() {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(8.0);
|
||||
let label = format!("{} {}", SEAL_CHECK, t!("wallets.payment_proof"));
|
||||
let text_color = Colors::gold_dark();
|
||||
let btn_color = Colors::white_or_black(false);
|
||||
// Draw button to show payment proof sharing content.
|
||||
View::colored_text_button(ui, label, text_color, btn_color, || {
|
||||
if let Ok(p) = serde_json::to_string_pretty(&tx.proof) {
|
||||
let c = PaymentProofContent::new(Some(p));
|
||||
self.proof_content = Some(c);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ui.add_space(8.0);
|
||||
@@ -125,6 +156,7 @@ impl WalletTransactionContent {
|
||||
if m.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let amount = amount_to_hr_string(tx.amount, true);
|
||||
let desc_text = if tx.can_finalize() {
|
||||
if tx.data.tx_type == TxLogEntryType::TxSent {
|
||||
@@ -146,7 +178,7 @@ impl WalletTransactionContent {
|
||||
ui.add_space(6.0);
|
||||
|
||||
let mut message = m.clone();
|
||||
// Draw slatepack message content.
|
||||
// Draw Slatepack message content.
|
||||
ui.vertical_centered(|ui| {
|
||||
let scroll_id = Id::from("tx_info_message_request").with(tx.data.id);
|
||||
View::horizontal_line(ui, Colors::item_stroke());
|
||||
@@ -165,7 +197,7 @@ impl WalletTransactionContent {
|
||||
.desired_rows(5)
|
||||
.interactive(false)
|
||||
.desired_width(f32::INFINITY)
|
||||
.show(ui).response;
|
||||
.show(ui);
|
||||
ui.add_space(6.0);
|
||||
});
|
||||
});
|
||||
@@ -255,7 +287,7 @@ impl WalletTransactionContent {
|
||||
if tx.can_cancel() || rebroadcast {
|
||||
let r = View::item_rounding(0, 2, true);
|
||||
View::item_button(ui, r, PROHIBIT, Some(Colors::red()), || {
|
||||
wallet.task(WalletTask::Cancel(tx.clone()));
|
||||
wallet.task(WalletTask::Cancel(tx.data.clone()));
|
||||
Modal::close();
|
||||
});
|
||||
}
|
||||
@@ -282,10 +314,15 @@ impl WalletTransactionContent {
|
||||
let label = format!("{} {}", FILE_ARCHIVE, t!("kernel"));
|
||||
info_item_ui(ui, kernel.0.to_hex(), label, true, cb);
|
||||
}
|
||||
// Show receiver address.
|
||||
if let Some(rec) = &tx.receiver {
|
||||
// Show receiver or sender address.
|
||||
let addr = if tx.data.tx_type == TxLogEntryType::TxSent {
|
||||
&tx.receiver
|
||||
} else {
|
||||
&tx.sender
|
||||
};
|
||||
if let Some(addr) = addr {
|
||||
let label = format!("{} {}", CIRCLE_HALF, t!("network_mining.address"));
|
||||
info_item_ui(ui, rec.to_string(), label, true, cb);
|
||||
info_item_ui(ui, addr.to_string(), label, true, cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user