wallet: fix for already canceled invoice, transaction info modal, ability to finalize from list
This commit is contained in:
parent
92e1da511d
commit
01b5b21488
10 changed files with 591 additions and 157 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -2808,7 +2808,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "grin_wallet_api"
|
||||
version = "5.2.0-beta.1"
|
||||
source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#75363a9a258bc1fb0cf60bfb4c88a8a653b122f2"
|
||||
source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702"
|
||||
dependencies = [
|
||||
"base64 0.12.3",
|
||||
"chrono",
|
||||
|
@ -2833,7 +2833,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "grin_wallet_config"
|
||||
version = "5.2.0-beta.1"
|
||||
source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#75363a9a258bc1fb0cf60bfb4c88a8a653b122f2"
|
||||
source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702"
|
||||
dependencies = [
|
||||
"dirs 2.0.2",
|
||||
"grin_core",
|
||||
|
@ -2848,7 +2848,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "grin_wallet_controller"
|
||||
version = "5.2.0-beta.1"
|
||||
source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#75363a9a258bc1fb0cf60bfb4c88a8a653b122f2"
|
||||
source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"easy-jsonrpc-mw",
|
||||
|
@ -2882,7 +2882,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "grin_wallet_impls"
|
||||
version = "5.2.0-beta.1"
|
||||
source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#75363a9a258bc1fb0cf60bfb4c88a8a653b122f2"
|
||||
source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702"
|
||||
dependencies = [
|
||||
"base64 0.12.3",
|
||||
"blake2-rfc",
|
||||
|
@ -2921,7 +2921,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "grin_wallet_libwallet"
|
||||
version = "5.2.0-beta.1"
|
||||
source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#75363a9a258bc1fb0cf60bfb4c88a8a653b122f2"
|
||||
source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702"
|
||||
dependencies = [
|
||||
"age",
|
||||
"base64 0.9.3",
|
||||
|
@ -2958,7 +2958,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "grin_wallet_util"
|
||||
version = "5.2.0-beta.1"
|
||||
source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#75363a9a258bc1fb0cf60bfb4c88a8a653b122f2"
|
||||
source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702"
|
||||
dependencies = [
|
||||
"data-encoding",
|
||||
"ed25519-dalek",
|
||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -26,11 +26,11 @@ grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "master
|
|||
grin_util = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
|
||||
## wallet
|
||||
grin_wallet_impls = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" }
|
||||
grin_wallet_api = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" }
|
||||
grin_wallet_libwallet = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" }
|
||||
grin_wallet_util = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" }
|
||||
grin_wallet_controller = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" }
|
||||
grin_wallet_impls = { git = "https://github.com/yeastplume/grin-wallet", branch = "prevent_double_pay" }
|
||||
grin_wallet_api = { git = "https://github.com/yeastplume/grin-wallet", branch = "prevent_double_pay" }
|
||||
grin_wallet_libwallet = { git = "https://github.com/yeastplume/grin-wallet", branch = "prevent_double_pay" }
|
||||
grin_wallet_util = { git = "https://github.com/yeastplume/grin-wallet", branch = "prevent_double_pay" }
|
||||
grin_wallet_controller = { git = "https://github.com/yeastplume/grin-wallet", branch = "prevent_double_pay" }
|
||||
|
||||
## ui
|
||||
egui = { version = "0.27.2", default-features = false }
|
||||
|
|
|
@ -10,6 +10,8 @@ show: Show
|
|||
delete: Delete
|
||||
clear: Clear
|
||||
create: Create
|
||||
id: Identifier
|
||||
kernel: Kernel
|
||||
wallets:
|
||||
await_conf_amount: Awaiting confirmation
|
||||
await_fin_amount: Awaiting finalization
|
||||
|
@ -65,11 +67,11 @@ wallets:
|
|||
tx_finalizing: Finalizing
|
||||
tx_confirmed: Confirmed
|
||||
txs: Transactions
|
||||
input_finalize_desc: 'Enter message to finalize the transaction:'
|
||||
messages: Messages
|
||||
transport: Transport
|
||||
input_slatepack_desc: 'Enter message to create response or finalize the transaction:'
|
||||
send_slatepack_desc: 'Send message to receiver of funds to finalize the transaction:'
|
||||
parse_slatepack_err: 'An error occurred during handling of the message, check input data:'
|
||||
parse_slatepack_err: 'An error occurred during reading of the message, check input data:'
|
||||
pay_balance_error: 'Account balance is insufficient to pay %{amount} ツ and network fee.'
|
||||
parse_i1_slatepack_desc: 'To pay %{amount} ツ send this message to the receiver:'
|
||||
parse_i2_slatepack_desc: 'Finalize transaction to receive %{amount} ツ'
|
||||
|
@ -78,7 +80,8 @@ wallets:
|
|||
parse_s2_slatepack_desc: 'Finalize transaction to send %{amount} ツ'
|
||||
parse_s3_slatepack_desc: 'Post transaction to finalize sending of %{amount} ツ'
|
||||
resp_slatepack_err: 'An error occurred during creation of the response, check input data:'
|
||||
resp_exists_err: 'Such transaction already exists.'
|
||||
resp_exists_err: Such transaction already exists.
|
||||
resp_canceled_err: Such transaction was already canceled.
|
||||
create_request_desc: 'Create request to send or receive the funds:'
|
||||
send_request_desc: 'You have created a request to send %{amount} ツ. Send this message to the receiver:'
|
||||
send_slatepack_err: An error occurred during creation of request to send funds, check input data.
|
||||
|
|
|
@ -10,6 +10,8 @@ show: Показать
|
|||
delete: Удалить
|
||||
clear: Очистить
|
||||
create: Создать
|
||||
id: Идентификатор
|
||||
kernel: Ядро
|
||||
wallets:
|
||||
await_conf_amount: Ожидает подтверждения
|
||||
await_fin_amount: Ожидает завершения
|
||||
|
@ -65,11 +67,11 @@ wallets:
|
|||
tx_finalizing: Завершение
|
||||
tx_confirmed: Подтверждено
|
||||
txs: Транзакции
|
||||
input_finalize_desc: 'Введите полученное сообщение для завершения транзакции:'
|
||||
messages: Сообщения
|
||||
transport: Транспорт
|
||||
input_slatepack_desc: 'Введите полученное сообщение для создания ответа или завершения транзакции:'
|
||||
send_slatepack_desc: 'Отправьте сообщение получателю средств для завершения транзакции:'
|
||||
parse_slatepack_err: 'Во время обработки сообщения произошла ошибка, проверьте входные данные:'
|
||||
parse_slatepack_err: 'Во время чтения сообщения произошла ошибка, проверьте входные данные:'
|
||||
pay_balance_error: 'Средств на аккаунте недостаточно для оплаты %{amount} ツ и комиссии сети.'
|
||||
parse_i1_slatepack_desc: 'Для оплаты %{amount} ツ отправьте это сообщение получателю:'
|
||||
parse_i2_slatepack_desc: 'Завершите транзакцию для получения %{amount} ツ'
|
||||
|
@ -78,8 +80,9 @@ wallets:
|
|||
parse_s2_slatepack_desc: 'Завершите транзакцию для отправки %{amount} ツ'
|
||||
parse_s3_slatepack_desc: 'Опубликуйте транзакцию для завершения отправки %{amount} ツ'
|
||||
resp_slatepack_err: 'Во время создания ответа произошла ошибка, проверьте входные данные:'
|
||||
resp_exists_err: 'Такая транзакция уже существует.'
|
||||
create_request_desc: 'Cоздать запрос на отправку или получение средств:'
|
||||
resp_exists_err: Такая транзакция уже существует.
|
||||
resp_canceled_err: Такая транзакция уже была отменена.
|
||||
create_request_desc: 'Создайте запрос на отправку или получение средств:'
|
||||
send_request_desc: 'Вы создали запрос на отправку %{amount} ツ. Отправьте это сообщение получателю:'
|
||||
send_slatepack_err: Во время создания запроса на отправку средств произошла ошибка, проверьте входные данные.
|
||||
invoice_desc: 'Вы создали запрос на получение %{amount} ツ. Отправьте это сообщение отправителю:'
|
||||
|
|
|
@ -23,7 +23,7 @@ use crate::gui::icons::{BRIDGE, CHAT_CIRCLE_TEXT, CHECK, CHECK_FAT, FOLDER_USER,
|
|||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, Root, View};
|
||||
use crate::gui::views::types::{ModalPosition, TextEditOptions};
|
||||
use crate::gui::views::wallets::{WalletInfo, WalletMessages, WalletTransport, WalletSettings};
|
||||
use crate::gui::views::wallets::{WalletTransactions, WalletMessages, WalletTransport, WalletSettings};
|
||||
use crate::gui::views::wallets::types::{GRIN, WalletTab, WalletTabType};
|
||||
use crate::node::Node;
|
||||
use crate::wallet::{Wallet, WalletConfig};
|
||||
|
@ -52,7 +52,7 @@ impl Default for WalletContent {
|
|||
account_creating: false,
|
||||
account_label_edit: "".to_string(),
|
||||
account_creation_error: false,
|
||||
current_tab: Box::new(WalletInfo::default())
|
||||
current_tab: Box::new(WalletTransactions::default())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -349,7 +349,7 @@ impl WalletContent {
|
|||
ui.columns(4, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::tab_button(ui, GRAPH, current_type == WalletTabType::Txs, || {
|
||||
self.current_tab = Box::new(WalletInfo::default());
|
||||
self.current_tab = Box::new(WalletTransactions::default());
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
|
|
|
@ -146,14 +146,14 @@ impl WalletMessages {
|
|||
ui: &mut egui::Ui,
|
||||
wallet: &mut Wallet,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
ui.add_space(4.0);
|
||||
ui.add_space(3.0);
|
||||
|
||||
// Show creation of request to send or receive funds.
|
||||
self.request_ui(ui, cb);
|
||||
|
||||
ui.add_space(12.0);
|
||||
View::horizontal_line(ui, Colors::ITEM_STROKE);
|
||||
ui.add_space(8.0);
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Show Slatepack message input field.
|
||||
self.input_slatepack_ui(ui, wallet, cb);
|
||||
|
@ -326,11 +326,10 @@ impl WalletMessages {
|
|||
} else {
|
||||
show_dandelion = true;
|
||||
View::button(ui, t!("wallets.finalize"), Colors::GOLD, || {
|
||||
let message = self.message_edit.clone();
|
||||
let slate = self.message_slate.clone().unwrap();
|
||||
if slate.state == SlateState::Invoice3 ||
|
||||
slate.state == SlateState::Standard3 {
|
||||
if let Ok(_) = wallet.post(&slate, self.dandelion) {
|
||||
if wallet.post(&slate, self.dandelion).is_ok() {
|
||||
self.message_edit.clear();
|
||||
self.message_slate = None;
|
||||
} else {
|
||||
|
@ -341,7 +340,8 @@ impl WalletMessages {
|
|||
);
|
||||
}
|
||||
} else {
|
||||
if let Ok(_) = wallet.finalize(message, self.dandelion) {
|
||||
let r = wallet.finalize(&self.message_edit, self.dandelion);
|
||||
if r.is_ok() {
|
||||
self.message_edit.clear();
|
||||
self.message_slate = None;
|
||||
} else {
|
||||
|
@ -397,7 +397,7 @@ impl WalletMessages {
|
|||
if self.message_edit.is_empty() {
|
||||
return;
|
||||
}
|
||||
if let Ok(mut slate) = wallet.parse_slatepack(self.message_edit.clone()) {
|
||||
if let Ok(mut slate) = wallet.parse_slatepack(&self.message_edit) {
|
||||
println!("parse_message: {}", slate);
|
||||
|
||||
// Try to setup empty amount from transaction by id.
|
||||
|
@ -423,13 +423,23 @@ impl WalletMessages {
|
|||
match slate.state {
|
||||
SlateState::Standard1 | SlateState::Invoice1 => {
|
||||
let resp = if slate.state == SlateState::Standard1 {
|
||||
wallet.receive(self.message_edit.clone())
|
||||
wallet.receive(&self.message_edit)
|
||||
} else {
|
||||
wallet.pay(self.message_edit.clone())
|
||||
wallet.pay(&self.message_edit)
|
||||
};
|
||||
if resp.is_ok() {
|
||||
self.response_edit = resp.unwrap();
|
||||
} else {
|
||||
match resp.err().unwrap() {
|
||||
grin_wallet_libwallet::Error::TransactionWasCancelled {..} => {
|
||||
// Set already canceled transaction error message.
|
||||
self.message_error = Some(
|
||||
MessageError::Response(t!("wallets.resp_canceled_err"))
|
||||
);
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// Check if tx with same slate id already exists.
|
||||
let exists_tx = wallet.tx_by_slate(&slate).is_some();
|
||||
if exists_tx {
|
||||
|
@ -726,7 +736,7 @@ impl WalletMessages {
|
|||
// Button to cancel transaction.
|
||||
let cancel = format!("{} {}", PROHIBIT, t!("modal.cancel"));
|
||||
View::colored_text_button(ui, cancel, Colors::RED, Colors::BUTTON, || {
|
||||
if let Ok(slate) = wallet.parse_slatepack(self.request_edit.clone()) {
|
||||
if let Ok(slate) = wallet.parse_slatepack(&self.request_edit) {
|
||||
if let Some(tx) = wallet.tx_by_slate(&slate) {
|
||||
wallet.cancel(tx.data.id);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
pub mod types;
|
||||
|
||||
mod txs;
|
||||
pub use txs::WalletInfo;
|
||||
pub use txs::WalletTransactions;
|
||||
|
||||
mod messages;
|
||||
pub use messages::WalletMessages;
|
||||
|
|
|
@ -15,24 +15,50 @@
|
|||
use egui::{Align, Id, Layout, Margin, RichText, Rounding, ScrollArea};
|
||||
use egui::scroll_area::ScrollBarVisibility;
|
||||
use grin_core::core::amount_to_hr_string;
|
||||
use grin_util::ToHex;
|
||||
use grin_wallet_libwallet::{Slate, SlateState, TxLogEntryType};
|
||||
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{ARROW_CIRCLE_DOWN, ARROW_CIRCLE_UP, ARROW_CLOCKWISE, ARROWS_CLOCKWISE, BRIDGE, CALENDAR_CHECK, CHAT_CIRCLE_TEXT, CHECK_CIRCLE, DOTS_THREE_CIRCLE, FILE_TEXT, GEAR_FINE, PROHIBIT, X_CIRCLE};
|
||||
use crate::gui::icons::{ARROW_CIRCLE_DOWN, ARROW_CIRCLE_UP, ARROW_CLOCKWISE, BRIDGE, CALENDAR_CHECK, CHAT_CIRCLE_TEXT, CHECK, CHECK_CIRCLE, CLIPBOARD_TEXT, COPY, DOTS_THREE_CIRCLE, FILE_ARCHIVE, FILE_TEXT, GEAR_FINE, HASH_STRAIGHT, PROHIBIT, X_CIRCLE};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Root, View};
|
||||
use crate::gui::views::{Modal, Root, View};
|
||||
use crate::gui::views::types::ModalPosition;
|
||||
use crate::gui::views::wallets::types::WalletTab;
|
||||
use crate::gui::views::wallets::wallet::types::{GRIN, WalletTabType};
|
||||
use crate::gui::views::wallets::wallet::types::{GRIN, SLATEPACK_MESSAGE_HINT, WalletTabType};
|
||||
use crate::gui::views::wallets::wallet::WalletContent;
|
||||
use crate::wallet::types::{WalletData, WalletTransaction};
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
/// Wallet transactions tab content.
|
||||
pub struct WalletTransactions {
|
||||
/// Transaction identifier to user at [`Modal`].
|
||||
tx_info_id: Option<u32>,
|
||||
/// Transaction [`Slate`] to use at [`Modal`].
|
||||
tx_info_slate: Option<Slate>,
|
||||
/// Response Slatepack message input value at [`Modal`].
|
||||
tx_info_response_edit: String,
|
||||
/// Finalization Slatepack message input value at [`Modal`].
|
||||
tx_info_finalize_edit: String,
|
||||
/// Flag to check if error happened during transaction finalization at [`Modal`].
|
||||
tx_info_finalize_error: bool,
|
||||
/// Flag to check if tx finalization requested at [`Modal`].
|
||||
tx_info_finalize: bool,
|
||||
}
|
||||
|
||||
/// Wallet info tab content.
|
||||
#[derive(Default)]
|
||||
pub struct WalletInfo;
|
||||
impl Default for WalletTransactions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
tx_info_id: None,
|
||||
tx_info_slate: None,
|
||||
tx_info_response_edit: "".to_string(),
|
||||
tx_info_finalize_edit: "".to_string(),
|
||||
tx_info_finalize_error: false,
|
||||
tx_info_finalize: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WalletTab for WalletInfo {
|
||||
impl WalletTab for WalletTransactions {
|
||||
fn get_type(&self) -> WalletTabType {
|
||||
WalletTabType::Txs
|
||||
}
|
||||
|
@ -41,11 +67,14 @@ impl WalletTab for WalletInfo {
|
|||
ui: &mut egui::Ui,
|
||||
_: &mut eframe::Frame,
|
||||
wallet: &mut Wallet,
|
||||
_: &dyn PlatformCallbacks) {
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
if WalletContent::sync_ui(ui, wallet) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show modal content for this ui container.
|
||||
self.modal_content_ui(ui, wallet, cb);
|
||||
|
||||
// Show wallet transactions panel.
|
||||
egui::CentralPanel::default()
|
||||
.frame(egui::Frame {
|
||||
|
@ -62,22 +91,31 @@ impl WalletTab for WalletInfo {
|
|||
.show_inside(ui, |ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
let data = wallet.get_data().unwrap();
|
||||
self.txs_ui(ui, wallet, &data);
|
||||
self.txs_ui(ui, wallet, &data, cb);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl WalletInfo {
|
||||
/// Identifier for transaction information [`Modal`].
|
||||
const TX_INFO_MODAL: &'static str = "tx_info_modal";
|
||||
|
||||
/// Height of transaction list item.
|
||||
const TX_ITEM_HEIGHT: f32 = 76.0;
|
||||
|
||||
impl WalletTransactions {
|
||||
/// Draw transactions content.
|
||||
fn txs_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet, data: &WalletData) {
|
||||
let txs_size = data.txs.len();
|
||||
fn txs_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
wallet: &mut Wallet,
|
||||
data: &WalletData,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
let amount_awaiting_conf = data.info.amount_awaiting_confirmation;
|
||||
let amount_awaiting_fin = data.info.amount_awaiting_finalization;
|
||||
let amount_locked = data.info.amount_locked;
|
||||
|
||||
// Show transactions info.
|
||||
View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| {
|
||||
let amount_awaiting_conf = data.info.amount_awaiting_confirmation;
|
||||
let amount_awaiting_fin = data.info.amount_awaiting_finalization;
|
||||
let amount_locked = data.info.amount_locked;
|
||||
|
||||
// Show non-zero awaiting confirmation amount.
|
||||
if amount_awaiting_conf != 0 {
|
||||
|
@ -117,7 +155,7 @@ impl WalletInfo {
|
|||
}
|
||||
|
||||
// Show message when wallet txs are empty.
|
||||
if txs_size == 0 {
|
||||
if data.txs.is_empty() {
|
||||
View::center_content(ui, 96.0, |ui| {
|
||||
let empty_text = t!(
|
||||
"wallets.txs_empty",
|
||||
|
@ -137,105 +175,139 @@ impl WalletInfo {
|
|||
.scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible)
|
||||
.id_source(Id::from("txs_content").with(wallet.get_config().id))
|
||||
.auto_shrink([false; 2])
|
||||
.show_rows(ui, TX_ITEM_HEIGHT, txs_size, |ui, row_range| {
|
||||
.show_rows(ui, TX_ITEM_HEIGHT, data.txs.len(), |ui, row_range| {
|
||||
ui.add_space(3.0);
|
||||
View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| {
|
||||
let amount_awaiting_conf = data.info.amount_awaiting_confirmation;
|
||||
let amount_awaiting_fin = data.info.amount_awaiting_finalization;
|
||||
let amount_locked = data.info.amount_locked;
|
||||
let extra_padding = amount_awaiting_conf != 0 || amount_awaiting_fin != 0 ||
|
||||
amount_locked != 0;
|
||||
for index in row_range {
|
||||
let tx = data.txs.get(index).unwrap();
|
||||
// Setup item rounding.
|
||||
let item_rounding = View::item_rounding(index, txs_size, false);
|
||||
// Show transaction item.
|
||||
tx_item_ui(ui, tx, item_rounding, extra_padding, &data, wallet);
|
||||
let tx = data.txs.get(index).unwrap();
|
||||
let rounding = View::item_rounding(index, data.txs.len(), false);
|
||||
self.tx_item_ui(ui, tx, rounding, extra_padding, true, &data, wallet, cb);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Height of transaction list item.
|
||||
const TX_ITEM_HEIGHT: f32 = 76.0;
|
||||
|
||||
/// Draw transaction item.
|
||||
fn tx_item_ui(ui: &mut egui::Ui,
|
||||
tx: &WalletTransaction,
|
||||
mut rounding: Rounding,
|
||||
extra_padding: bool,
|
||||
data: &WalletData,
|
||||
wallet: &mut Wallet) {
|
||||
// Setup layout size.
|
||||
let mut rect = ui.available_rect_before_wrap();
|
||||
if extra_padding {
|
||||
rect.min += egui::emath::vec2(6.0, 0.0);
|
||||
rect.max -= egui::emath::vec2(6.0, 0.0);
|
||||
/// Draw [`Modal`] content for this ui container.
|
||||
fn modal_content_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
wallet: &mut Wallet,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
match Modal::opened() {
|
||||
None => {}
|
||||
Some(id) => {
|
||||
match id {
|
||||
TX_INFO_MODAL => {
|
||||
Modal::ui(ui.ctx(), |ui, modal| {
|
||||
self.tx_info_modal_ui(ui, wallet, modal, cb);
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
rect.set_height(TX_ITEM_HEIGHT);
|
||||
|
||||
// Draw round background.
|
||||
let bg_rect = rect.clone();
|
||||
ui.painter().rect(bg_rect, rounding, Colors::BUTTON, View::ITEM_STROKE);
|
||||
/// Draw transaction item.
|
||||
fn tx_item_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
tx: &WalletTransaction,
|
||||
mut rounding: Rounding,
|
||||
extra_padding: bool,
|
||||
can_show_info: bool,
|
||||
data: &WalletData,
|
||||
wallet: &mut Wallet,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
// Setup layout size.
|
||||
let mut rect = ui.available_rect_before_wrap();
|
||||
if extra_padding {
|
||||
rect.min += egui::emath::vec2(6.0, 0.0);
|
||||
rect.max -= egui::emath::vec2(6.0, 0.0);
|
||||
}
|
||||
rect.set_height(TX_ITEM_HEIGHT);
|
||||
|
||||
// Draw round background.
|
||||
let bg_rect = rect.clone();
|
||||
let color = if can_show_info {
|
||||
Colors::BUTTON
|
||||
} else {
|
||||
Colors::FILL
|
||||
};
|
||||
ui.painter().rect(bg_rect, rounding, color, View::ITEM_STROKE);
|
||||
|
||||
ui.vertical(|ui| {
|
||||
ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| {
|
||||
if extra_padding {
|
||||
ui.add_space(-6.0);
|
||||
// Draw button to show transaction info.
|
||||
if can_show_info {
|
||||
rounding.nw = 0.0;
|
||||
rounding.sw = 0.0;
|
||||
View::item_button(ui, rounding, FILE_TEXT, None, || {
|
||||
self.tx_info_finalize = false;
|
||||
self.show_tx_info_modal(wallet, tx);
|
||||
});
|
||||
}
|
||||
|
||||
// Draw button to show transaction info.
|
||||
rounding.nw = 0.0;
|
||||
rounding.sw = 0.0;
|
||||
View::item_button(ui, rounding, FILE_TEXT, None, || {
|
||||
//TODO: Show tx info
|
||||
});
|
||||
|
||||
// Setup flag to repost unconfirmed posting transaction after min confirmation time.
|
||||
let last_height = data.info.last_confirmed_height;
|
||||
let min_conf = data.info.minimum_confirmations;
|
||||
let can_repost = tx.posting && tx.repost_height.is_some() &&
|
||||
last_height - tx.repost_height.unwrap() > min_conf;
|
||||
|
||||
// Draw cancel button for txs to repost or also non-cancelled, non-posting.
|
||||
if can_repost || (!tx.posting && !tx.data.confirmed &&
|
||||
tx.data.tx_type != TxLogEntryType::TxReceivedCancelled
|
||||
&& tx.data.tx_type != TxLogEntryType::TxSentCancelled) {
|
||||
View::item_button(ui, Rounding::default(), PROHIBIT, Some(Colors::RED), || {
|
||||
// Draw cancel button for tx that can be reposted and canceled.
|
||||
if tx.can_repost(data) || tx.can_cancel() {
|
||||
let cancel_rounding = if can_show_info {
|
||||
Rounding::default()
|
||||
} else {
|
||||
rounding.nw = 0.0;
|
||||
rounding.sw = 0.0;
|
||||
rounding
|
||||
};
|
||||
View::item_button(ui, cancel_rounding, PROHIBIT, Some(Colors::RED), || {
|
||||
wallet.cancel(tx.data.id);
|
||||
});
|
||||
}
|
||||
|
||||
// Draw button to repost transaction.
|
||||
if can_repost {
|
||||
View::item_button(ui,
|
||||
Rounding::default(),
|
||||
ARROW_CLOCKWISE,
|
||||
Some(Colors::GREEN), || {
|
||||
// Create slate to check existing file.
|
||||
let mut slate = Slate::blank(1, false);
|
||||
slate.id = tx.data.tx_slate_id.unwrap();
|
||||
slate.state = match tx.data.tx_type {
|
||||
TxLogEntryType::TxReceived => SlateState::Invoice3,
|
||||
_ => SlateState::Standard3
|
||||
};
|
||||
// Post tx after getting slate from slatepack file.
|
||||
if let Some(sp) = wallet.read_slatepack(&slate) {
|
||||
if let Ok(s) = wallet.parse_slatepack(sp) {
|
||||
let _ = wallet.post(&s, wallet.can_use_dandelion());
|
||||
}
|
||||
// Draw finalization button for tx that can be finalized.
|
||||
if tx.can_finalize {
|
||||
let (icon, color) = if !can_show_info && self.tx_info_finalize {
|
||||
(FILE_TEXT, None)
|
||||
} else {
|
||||
(CHECK, Some(Colors::GREEN))
|
||||
};
|
||||
View::item_button(ui, Rounding::default(), icon, color, || {
|
||||
if !can_show_info && self.tx_info_finalize {
|
||||
self.tx_info_finalize = false;
|
||||
return;
|
||||
}
|
||||
self.tx_info_finalize = true;
|
||||
// Show transaction information modal.
|
||||
if can_show_info {
|
||||
self.show_tx_info_modal(wallet, tx);
|
||||
cb.show_keyboard();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Draw button to repost transaction.
|
||||
if tx.can_repost(data) {
|
||||
View::item_button(ui,
|
||||
Rounding::default(),
|
||||
ARROW_CLOCKWISE,
|
||||
Some(Colors::GREEN), || {
|
||||
// Create slate to check existing file.
|
||||
let mut slate = Slate::blank(1, false);
|
||||
slate.id = tx.data.tx_slate_id.unwrap();
|
||||
slate.state = match tx.data.tx_type {
|
||||
TxLogEntryType::TxReceived => SlateState::Invoice3,
|
||||
_ => SlateState::Standard3
|
||||
};
|
||||
// Post tx after getting slate from slatepack file.
|
||||
if let Some(sp) = wallet.read_slatepack(&slate) {
|
||||
if let Ok(s) = wallet.parse_slatepack(&sp) {
|
||||
let _ = wallet.post(&s, wallet.can_use_dandelion());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let layout_size = ui.available_size();
|
||||
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
|
||||
if extra_padding {
|
||||
ui.add_space(12.0);
|
||||
} else {
|
||||
ui.add_space(6.0);
|
||||
}
|
||||
ui.add_space(6.0);
|
||||
ui.vertical(|ui| {
|
||||
ui.add_space(3.0);
|
||||
|
||||
|
@ -294,13 +366,14 @@ fn tx_item_ui(ui: &mut egui::Ui,
|
|||
format!("{} {}", CHECK_CIRCLE, t!("wallets.tx_confirmed"))
|
||||
},
|
||||
TxLogEntryType::TxSent | TxLogEntryType::TxReceived => {
|
||||
let min_conf = data.info.minimum_confirmations;
|
||||
if data.info.last_confirmed_height - tx_height > min_conf {
|
||||
let (icon, text) = if tx.data.tx_type == TxLogEntryType::TxSent {
|
||||
let (i, t) = if tx.data.tx_type == TxLogEntryType::TxSent {
|
||||
(ARROW_CIRCLE_UP, t!("wallets.tx_sent"))
|
||||
} else {
|
||||
(ARROW_CIRCLE_DOWN, t!("wallets.tx_received"))
|
||||
};
|
||||
format!("{} {}", icon, text)
|
||||
format!("{} {}", i, t)
|
||||
} else {
|
||||
let h = data.info.last_confirmed_height;
|
||||
let left_conf = h - tx_height;
|
||||
|
@ -343,8 +416,269 @@ fn tx_item_ui(ui: &mut egui::Ui,
|
|||
let tx_time = View::format_time(tx.data.creation_ts.timestamp());
|
||||
let tx_time_text = format!("{} {}", CALENDAR_CHECK, tx_time);
|
||||
ui.label(RichText::new(tx_time_text).size(15.0).color(Colors::GRAY));
|
||||
ui.add_space(3.0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Show transaction information [`Modal`].
|
||||
fn show_tx_info_modal(&mut self, wallet: &Wallet, tx: &WalletTransaction) {
|
||||
self.tx_info_response_edit = "".to_string();
|
||||
self.tx_info_finalize_edit = "".to_string();
|
||||
self.tx_info_finalize_error = false;
|
||||
self.tx_info_id = Some(tx.data.id);
|
||||
// Setup slate and message from transaction.
|
||||
if let Some((slate, message)) = wallet.read_slate_by_tx(tx) {
|
||||
self.tx_info_response_edit = message;
|
||||
self.tx_info_slate = Some(slate);
|
||||
}
|
||||
// Show transaction information modal.
|
||||
Modal::new(TX_INFO_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.show();
|
||||
}
|
||||
|
||||
/// Draw transaction info [`Modal`] content.
|
||||
fn tx_info_modal_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
wallet: &mut Wallet,
|
||||
modal: &Modal,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
// Check values and setup transaction data.
|
||||
let wallet_data = wallet.get_data();
|
||||
if wallet_data.is_none() {
|
||||
modal.close();
|
||||
return;
|
||||
}
|
||||
let data = wallet_data.unwrap();
|
||||
let tx_id = self.tx_info_id.unwrap();
|
||||
let txs = data.txs.iter()
|
||||
.filter(|tx| tx.data.id == tx_id)
|
||||
.collect::<Vec<&WalletTransaction>>();
|
||||
if txs.is_empty() {
|
||||
modal.close();
|
||||
return;
|
||||
}
|
||||
let tx = txs.get(0).unwrap();
|
||||
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Show transaction amount status and time.
|
||||
let rounding = View::item_rounding(0, 2, false);
|
||||
self.tx_item_ui(ui, tx, rounding, false, false, &data, wallet, cb);
|
||||
|
||||
// Show transaction ID info.
|
||||
if let Some(id) = tx.data.tx_slate_id {
|
||||
let label = format!("{} {}", HASH_STRAIGHT, t!("id"));
|
||||
Self::tx_info_modal_item_ui(ui, id.to_string(), label, true, cb);
|
||||
}
|
||||
// Show transaction kernel info.
|
||||
if let Some(kernel) = tx.data.kernel_excess {
|
||||
let label = format!("{} {}", FILE_ARCHIVE, t!("kernel"));
|
||||
Self::tx_info_modal_item_ui(ui, kernel.0.to_hex(), label, true, cb);
|
||||
}
|
||||
|
||||
// Show transaction Slatepack message response or finalization input.
|
||||
if !tx.posting && !tx.data.confirmed && (tx.data.tx_type == TxLogEntryType::TxSent ||
|
||||
tx.data.tx_type == TxLogEntryType::TxReceived) {
|
||||
self.tx_info_modal_slate_ui(ui, tx, wallet, modal, cb);
|
||||
}
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Show button to close modal.
|
||||
ui.vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("close"), Colors::WHITE, || {
|
||||
self.tx_info_id = None;
|
||||
self.tx_info_finalize = false;
|
||||
cb.hide_keyboard();
|
||||
modal.close();
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
}
|
||||
|
||||
/// Draw transaction information [`Modal`] item content.
|
||||
fn tx_info_modal_item_ui(ui: &mut egui::Ui,
|
||||
value: String,
|
||||
label: String,
|
||||
copy: bool,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
// Setup layout size.
|
||||
let mut rect = ui.available_rect_before_wrap();
|
||||
rect.set_height(50.0);
|
||||
|
||||
// Draw round background.
|
||||
let bg_rect = rect.clone();
|
||||
let mut rounding = View::item_rounding(1, 3, false);
|
||||
|
||||
ui.painter().rect(bg_rect, rounding, Colors::FILL, View::ITEM_STROKE);
|
||||
|
||||
ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| {
|
||||
// Draw button to copy transaction info value.
|
||||
if copy {
|
||||
rounding.nw = 0.0;
|
||||
rounding.sw = 0.0;
|
||||
View::item_button(ui, rounding, COPY, None, || {
|
||||
cb.copy_string_to_buffer(value.clone());
|
||||
});
|
||||
}
|
||||
|
||||
// Draw value information.
|
||||
let layout_size = ui.available_size();
|
||||
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
|
||||
ui.add_space(6.0);
|
||||
ui.vertical(|ui| {
|
||||
ui.add_space(3.0);
|
||||
View::ellipsize_text(ui, value, 15.0, Colors::TITLE);
|
||||
ui.label(RichText::new(label).size(15.0).color(Colors::GRAY));
|
||||
ui.add_space(3.0);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Draw Slate content to show response or generate payment proof.
|
||||
fn tx_info_modal_slate_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
tx: &WalletTransaction,
|
||||
wallet: &Wallet,
|
||||
modal: &Modal,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
if self.tx_info_slate.is_none() {
|
||||
return;
|
||||
}
|
||||
let slate = self.tx_info_slate.clone().unwrap();
|
||||
let amount = amount_to_hr_string(tx.amount, true);
|
||||
|
||||
// Draw Slatepack message input or output description text.
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
if self.tx_info_finalize {
|
||||
let desc_text = if self.tx_info_finalize_error {
|
||||
t!("wallets.finalize_slatepack_err")
|
||||
} else {
|
||||
if tx.data.tx_type == TxLogEntryType::TxSent {
|
||||
t!("wallets.parse_s2_slatepack_desc", "amount" => amount)
|
||||
} else {
|
||||
t!("wallets.parse_i2_slatepack_desc", "amount" => amount)
|
||||
}
|
||||
};
|
||||
let desc_color = if self.tx_info_finalize_error {
|
||||
Colors::RED
|
||||
} else {
|
||||
Colors::INACTIVE_TEXT
|
||||
};
|
||||
ui.label(RichText::new(desc_text).size(16.0).color(desc_color));
|
||||
} else {
|
||||
let desc_text = if tx.can_finalize {
|
||||
if tx.data.tx_type == TxLogEntryType::TxSent {
|
||||
t!("wallets.send_request_desc", "amount" => amount)
|
||||
} else {
|
||||
t!("wallets.invoice_desc", "amount" => amount)
|
||||
}
|
||||
} else {
|
||||
if tx.data.tx_type == TxLogEntryType::TxSent {
|
||||
t!("wallets.parse_i1_slatepack_desc", "amount" => amount)
|
||||
} else {
|
||||
t!("wallets.parse_s1_slatepack_desc", "amount" => amount)
|
||||
}
|
||||
};
|
||||
ui.label(RichText::new(desc_text).size(16.0).color(Colors::INACTIVE_TEXT));
|
||||
}
|
||||
});
|
||||
ui.add_space(4.0);
|
||||
|
||||
ui.vertical_centered(|ui| {
|
||||
let message_edit = if self.tx_info_finalize {
|
||||
&mut self.tx_info_finalize_edit
|
||||
} else {
|
||||
&mut self.tx_info_response_edit
|
||||
};
|
||||
let message_before = message_edit.clone();
|
||||
|
||||
// Draw Slatepack message text input or output.
|
||||
let input_id = Id::from("tx_info_slatepack_message").with(slate.id).with(tx.data.id);
|
||||
View::horizontal_line(ui, Colors::ITEM_STROKE);
|
||||
ui.add_space(3.0);
|
||||
ScrollArea::vertical()
|
||||
.max_height(128.0)
|
||||
.id_source(input_id)
|
||||
.auto_shrink([false; 2])
|
||||
.show(ui, |ui| {
|
||||
ui.add_space(7.0);
|
||||
egui::TextEdit::multiline(message_edit)
|
||||
.font(egui::TextStyle::Small)
|
||||
.desired_rows(5)
|
||||
.interactive(self.tx_info_finalize)
|
||||
.hint_text(SLATEPACK_MESSAGE_HINT)
|
||||
.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);
|
||||
|
||||
if self.tx_info_finalize {
|
||||
// Draw paste button.
|
||||
let paste_text = format!("{} {}", CLIPBOARD_TEXT, t!("paste"));
|
||||
View::button(ui, paste_text, Colors::BUTTON, || {
|
||||
self.tx_info_finalize_edit = cb.get_string_from_buffer();
|
||||
});
|
||||
|
||||
// Callback on finalization message input change.
|
||||
if message_before != self.tx_info_finalize_edit {
|
||||
self.on_finalization_input_change(tx, wallet, cb);
|
||||
}
|
||||
} else {
|
||||
// Draw copy button.
|
||||
let copy_text = format!("{} {}", COPY, t!("copy"));
|
||||
View::button(ui, copy_text, Colors::BUTTON, || {
|
||||
cb.copy_string_to_buffer(self.tx_info_response_edit.clone());
|
||||
self.tx_info_finalize_edit = "".to_string();
|
||||
if tx.can_finalize {
|
||||
self.tx_info_finalize = true;
|
||||
} else {
|
||||
modal.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Parse Slatepack message on transaction finalization input change.
|
||||
fn on_finalization_input_change(&mut self,
|
||||
tx: &WalletTransaction,
|
||||
wallet: &Wallet,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
let message = &self.tx_info_finalize_edit;
|
||||
if message.is_empty() {
|
||||
self.tx_info_finalize_error = false;
|
||||
} else {
|
||||
if let Ok(slate) = wallet.parse_slatepack(message) {
|
||||
let send = slate.state == SlateState::Standard2 &&
|
||||
tx.data.tx_type == TxLogEntryType::TxSent;
|
||||
let receive = slate.state == SlateState::Invoice2 &&
|
||||
tx.data.tx_type == TxLogEntryType::TxReceived;
|
||||
if Some(slate.id) == tx.data.tx_slate_id && (send || receive) {
|
||||
match wallet.finalize(message, wallet.can_use_dandelion()) {
|
||||
Ok(_) => {
|
||||
self.tx_info_finalize = false;
|
||||
self.tx_info_finalize_edit = "".to_string();
|
||||
cb.hide_keyboard();
|
||||
}
|
||||
Err(_) => {
|
||||
self.tx_info_finalize_error = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.tx_info_finalize_error = true;
|
||||
}
|
||||
} else {
|
||||
self.tx_info_finalize_error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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::{TxLogEntry, WalletInfo, WalletInst};
|
||||
use grin_wallet_libwallet::{TxLogEntry, TxLogEntryType, WalletInfo, WalletInst};
|
||||
|
||||
/// Mnemonic phrase setup mode.
|
||||
#[derive(PartialEq, Clone)]
|
||||
|
@ -149,6 +149,25 @@ pub struct WalletTransaction {
|
|||
pub amount: u64,
|
||||
/// Flag to check if transaction is posting after finalization.
|
||||
pub posting: bool,
|
||||
/// Flag to check if transaction can be finalized based on Slatepack message state.
|
||||
pub can_finalize: bool,
|
||||
/// Last wallet block height of transaction reposting.
|
||||
pub repost_height: Option<u64>
|
||||
}
|
||||
|
||||
impl WalletTransaction {
|
||||
/// Check if transaction can be cancelled.
|
||||
pub fn can_cancel(&self) -> bool {
|
||||
!self.posting && !self.data.confirmed &&
|
||||
self.data.tx_type != TxLogEntryType::TxReceivedCancelled
|
||||
&& self.data.tx_type != TxLogEntryType::TxSentCancelled
|
||||
}
|
||||
|
||||
/// Check if transaction can be reposted.
|
||||
pub fn can_repost(&self, data: &WalletData) -> bool {
|
||||
let last_height = data.info.last_confirmed_height;
|
||||
let min_conf = data.info.minimum_confirmations;
|
||||
self.posting && self.repost_height.is_some() &&
|
||||
last_height - self.repost_height.unwrap() > min_conf
|
||||
}
|
||||
}
|
|
@ -479,9 +479,9 @@ impl Wallet {
|
|||
}
|
||||
|
||||
/// Parse Slatepack message into [`Slate`].
|
||||
pub fn parse_slatepack(&self, message: String) -> Result<Slate, Error> {
|
||||
pub fn parse_slatepack(&self, message: &String) -> Result<Slate, Error> {
|
||||
let api = Owner::new(self.instance.clone().unwrap(), None);
|
||||
api.slate_from_slatepack_message(None, message, vec![])
|
||||
api.slate_from_slatepack_message(None, message.clone(), vec![])
|
||||
}
|
||||
|
||||
/// Create Slatepack message from provided slate.
|
||||
|
@ -493,7 +493,7 @@ impl Wallet {
|
|||
Ok(())
|
||||
})?;
|
||||
|
||||
// Save slatepack.
|
||||
// Write Slatepack message to file.
|
||||
let slatepack_dir = self.get_config().get_slatepack_path(&slate);
|
||||
let mut output = File::create(slatepack_dir)?;
|
||||
output.write_all(message.as_bytes())?;
|
||||
|
@ -510,7 +510,51 @@ impl Wallet {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get transaction by slate id.
|
||||
/// Get last stored [`Slate`] for transaction.
|
||||
pub fn read_slate_by_tx(&self, tx: &WalletTransaction) -> Option<(Slate, String)> {
|
||||
let mut slate = None;
|
||||
if let Some(slate_id) = tx.data.tx_slate_id {
|
||||
// Get slate state based on tx state and status.
|
||||
let state = if tx.posting {
|
||||
if tx.data.tx_type == TxLogEntryType::TxSent {
|
||||
Some(SlateState::Standard3)
|
||||
} else {
|
||||
Some(SlateState::Invoice3)
|
||||
}
|
||||
} else if !tx.data.confirmed && (tx.data.tx_type == TxLogEntryType::TxSent ||
|
||||
tx.data.tx_type == TxLogEntryType::TxReceived) {
|
||||
if tx.can_finalize {
|
||||
if tx.data.tx_type == TxLogEntryType::TxSent {
|
||||
Some(SlateState::Standard1)
|
||||
} else {
|
||||
Some(SlateState::Invoice1)
|
||||
}
|
||||
} else {
|
||||
if tx.data.tx_type == TxLogEntryType::TxReceived {
|
||||
Some(SlateState::Standard2)
|
||||
} else {
|
||||
Some(SlateState::Invoice2)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// Get slate from state by reading Slatepack message file.
|
||||
if let Some(st) = state {
|
||||
let mut s = Slate::blank(0, false);
|
||||
s.id = slate_id;
|
||||
s.state = st;
|
||||
if let Some(m) = self.read_slatepack(&s) {
|
||||
if let Ok(s) = self.parse_slatepack(&m) {
|
||||
slate = Some((s, m));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
slate
|
||||
}
|
||||
|
||||
/// Get transaction for [`Slate`] id.
|
||||
pub fn tx_by_slate(&self, slate: &Slate) -> Option<WalletTransaction> {
|
||||
if let Some(data) = self.get_data() {
|
||||
let txs = data.txs.clone().iter().map(|tx| tx.clone()).filter(|tx| {
|
||||
|
@ -571,7 +615,7 @@ impl Wallet {
|
|||
}
|
||||
|
||||
/// Handle message from the invoice issuer to send founds, return response for funds receiver.
|
||||
pub fn pay(&self, message: String) -> Result<String, Error> {
|
||||
pub fn pay(&self, message: &String) -> Result<String, Error> {
|
||||
let slate = self.parse_slatepack(message)?;
|
||||
let config = self.get_config();
|
||||
let args = InitTxArgs {
|
||||
|
@ -595,7 +639,7 @@ impl Wallet {
|
|||
}
|
||||
|
||||
/// Handle message to receive funds, return response to sender.
|
||||
pub fn receive(&self, message: String) -> Result<String, Error> {
|
||||
pub fn receive(&self, message: &String) -> Result<String, Error> {
|
||||
let mut slate = self.parse_slatepack(message)?;
|
||||
let api = Owner::new(self.instance.clone().unwrap(), None);
|
||||
controller::foreign_single_use(api.wallet_inst.clone(), None, |api| {
|
||||
|
@ -612,7 +656,7 @@ impl Wallet {
|
|||
}
|
||||
|
||||
/// Finalize transaction from provided message as sender or invoice issuer with Dandelion.
|
||||
pub fn finalize(&self, message: String, dandelion: bool) -> Result<Slate, Error> {
|
||||
pub fn finalize(&self, message: &String, dandelion: bool) -> Result<Slate, Error> {
|
||||
let mut slate = self.parse_slatepack(message)?;
|
||||
let api = Owner::new(self.instance.clone().unwrap(), None);
|
||||
slate = api.finalize_tx(None, &slate)?;
|
||||
|
@ -629,7 +673,7 @@ impl Wallet {
|
|||
// Post transaction to blockchain.
|
||||
let api = Owner::new(self.instance.clone().unwrap(), None);
|
||||
api.post_tx(None, slate, dandelion)?;
|
||||
// Setup transaction repost height and posting flag.
|
||||
// Setup transaction repost height, posting flag and ability to finalize.
|
||||
let mut slate = slate.clone();
|
||||
if slate.state == SlateState::Invoice2 {
|
||||
slate.state = SlateState::Invoice3
|
||||
|
@ -643,6 +687,7 @@ impl Wallet {
|
|||
if t.data.id == tx.data.id {
|
||||
t.repost_height = Some(data.info.last_confirmed_height);
|
||||
t.posting = true;
|
||||
t.can_finalize = false;
|
||||
}
|
||||
}
|
||||
*w_data = Some(data);
|
||||
|
@ -656,23 +701,23 @@ impl Wallet {
|
|||
pub fn cancel(&mut self, id: u32) {
|
||||
let instance = self.instance.clone().unwrap();
|
||||
let _ = cancel_tx(instance, None, &None, Some(id), None);
|
||||
// Set cancelling status.
|
||||
{
|
||||
let mut w_data = self.data.write().unwrap();
|
||||
let mut data = w_data.clone().unwrap();
|
||||
let txs = data.txs.iter_mut().map(|tx| {
|
||||
if tx.data.id == id {
|
||||
tx.data.tx_type = if tx.data.tx_type == TxLogEntryType::TxReceived {
|
||||
TxLogEntryType::TxReceivedCancelled
|
||||
} else {
|
||||
TxLogEntryType::TxSentCancelled
|
||||
};
|
||||
}
|
||||
tx.clone()
|
||||
}).collect::<Vec<WalletTransaction>>();
|
||||
data.txs = txs;
|
||||
*w_data = Some(data);
|
||||
}
|
||||
// Setup cancelling status, posting flag, and ability to finalize.
|
||||
let mut w_data = self.data.write().unwrap();
|
||||
let mut data = w_data.clone().unwrap();
|
||||
let txs = data.txs.iter_mut().map(|tx| {
|
||||
if tx.data.id == id {
|
||||
tx.posting = false;
|
||||
tx.can_finalize = false;
|
||||
tx.data.tx_type = if tx.data.tx_type == TxLogEntryType::TxReceived {
|
||||
TxLogEntryType::TxReceivedCancelled
|
||||
} else {
|
||||
TxLogEntryType::TxSentCancelled
|
||||
};
|
||||
}
|
||||
tx.clone()
|
||||
}).collect::<Vec<WalletTransaction>>();
|
||||
data.txs = txs;
|
||||
*w_data = Some(data);
|
||||
// Refresh wallet info to update statuses.
|
||||
self.sync();
|
||||
}
|
||||
|
@ -995,7 +1040,6 @@ fn sync_wallet_data(wallet: &Wallet) {
|
|||
// Create wallet txs.
|
||||
let mut new_txs: Vec<WalletTransaction> = vec![];
|
||||
for tx in &filter_txs {
|
||||
println!("{}", serde_json::to_string(tx).unwrap());
|
||||
// Setup transaction amount.
|
||||
let amount = if tx.amount_debited > tx.amount_credited {
|
||||
tx.amount_debited - tx.amount_credited
|
||||
|
@ -1003,15 +1047,20 @@ fn sync_wallet_data(wallet: &Wallet) {
|
|||
tx.amount_credited - tx.amount_debited
|
||||
};
|
||||
|
||||
// Setup transaction posting flag based on slate state.
|
||||
let posting = if (tx.tx_type == TxLogEntryType::TxSent ||
|
||||
tx.tx_type == TxLogEntryType::TxReceived) &&
|
||||
!tx.confirmed && tx.tx_slate_id.is_some() {
|
||||
let unconfirmed_sent_or_received = tx.tx_slate_id.is_some() &&
|
||||
!tx.confirmed && (tx.tx_type == TxLogEntryType::TxSent ||
|
||||
tx.tx_type == TxLogEntryType::TxReceived);
|
||||
|
||||
// Setup transaction posting status based on slate state.
|
||||
let posting = if unconfirmed_sent_or_received {
|
||||
println!("{}", serde_json::to_string(tx).unwrap());
|
||||
|
||||
// Create slate to check existing file.
|
||||
let mut slate = Slate::blank(1, false);
|
||||
let is_invoice = tx.tx_type == TxLogEntryType::TxReceived;
|
||||
let mut slate = Slate::blank(0, is_invoice);
|
||||
slate.id = tx.tx_slate_id.unwrap();
|
||||
slate.state = match tx.tx_type {
|
||||
TxLogEntryType::TxReceived => SlateState::Invoice3,
|
||||
slate.state = match is_invoice {
|
||||
true => SlateState::Invoice3,
|
||||
_ => SlateState::Standard3
|
||||
};
|
||||
|
||||
|
@ -1033,6 +1082,20 @@ fn sync_wallet_data(wallet: &Wallet) {
|
|||
false
|
||||
};
|
||||
|
||||
// Setup flag for ability to finalize transaction.
|
||||
let can_finalize = if !posting && unconfirmed_sent_or_received {
|
||||
// Create slate to check existing file.
|
||||
let mut slate = Slate::blank(1, false);
|
||||
slate.id = tx.tx_slate_id.unwrap();
|
||||
slate.state = match tx.tx_type {
|
||||
TxLogEntryType::TxReceived => SlateState::Invoice1,
|
||||
_ => SlateState::Standard1
|
||||
};
|
||||
wallet.read_slatepack(&slate).is_some()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
// Setup reposting height.
|
||||
let mut repost_height = None;
|
||||
if posting {
|
||||
|
@ -1046,10 +1109,12 @@ fn sync_wallet_data(wallet: &Wallet) {
|
|||
}
|
||||
}
|
||||
|
||||
// Add transaction to list.
|
||||
new_txs.push(WalletTransaction {
|
||||
data: tx.clone(),
|
||||
amount,
|
||||
posting,
|
||||
can_finalize,
|
||||
repost_height,
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue