ui: file picker button for slatepacks messages (text parsing), check if tx was synced from node, do not show txs actions on wallet loading
This commit is contained in:
parent
951886f5c7
commit
be80e82727
14 changed files with 199 additions and 27 deletions
|
@ -25,6 +25,7 @@ share: Share
|
||||||
theme: 'Theme:'
|
theme: 'Theme:'
|
||||||
dark: Dark
|
dark: Dark
|
||||||
light: Light
|
light: Light
|
||||||
|
choose_file: Choose file
|
||||||
wallets:
|
wallets:
|
||||||
await_conf_amount: Awaiting confirmation
|
await_conf_amount: Awaiting confirmation
|
||||||
await_fin_amount: Awaiting finalization
|
await_fin_amount: Awaiting finalization
|
||||||
|
|
|
@ -25,6 +25,7 @@ share: Поделиться
|
||||||
theme: 'Тема:'
|
theme: 'Тема:'
|
||||||
dark: Тёмная
|
dark: Тёмная
|
||||||
light: Светлая
|
light: Светлая
|
||||||
|
choose_file: Выбрать файл
|
||||||
wallets:
|
wallets:
|
||||||
await_conf_amount: Ожидает подтверждения
|
await_conf_amount: Ожидает подтверждения
|
||||||
await_fin_amount: Ожидает завершения
|
await_fin_amount: Ожидает завершения
|
||||||
|
|
|
@ -25,6 +25,7 @@ share: Paylasmak
|
||||||
theme: 'Tema:'
|
theme: 'Tema:'
|
||||||
dark: Karanlik
|
dark: Karanlik
|
||||||
light: Isik
|
light: Isik
|
||||||
|
choose_file: Dosya seçin
|
||||||
wallets:
|
wallets:
|
||||||
await_conf_amount: Onay bekleniyor
|
await_conf_amount: Onay bekleniyor
|
||||||
await_fin_amount: Tamamlanma bekleniyor
|
await_fin_amount: Tamamlanma bekleniyor
|
||||||
|
|
|
@ -33,6 +33,8 @@ const GREEN: Color32 = Color32::from_rgb(0, 0x64, 0);
|
||||||
|
|
||||||
const RED: Color32 = Color32::from_rgb(0x8B, 0, 0);
|
const RED: Color32 = Color32::from_rgb(0x8B, 0, 0);
|
||||||
|
|
||||||
|
const BLUE: Color32 = Color32::from_rgb(0, 0x66, 0xE4);
|
||||||
|
|
||||||
const FILL: Color32 = Color32::from_gray(244);
|
const FILL: Color32 = Color32::from_gray(244);
|
||||||
const FILL_DARK: Color32 = Color32::from_gray(24);
|
const FILL_DARK: Color32 = Color32::from_gray(24);
|
||||||
|
|
||||||
|
@ -132,6 +134,14 @@ impl Colors {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn blue() -> Color32 {
|
||||||
|
if use_dark() {
|
||||||
|
BLUE.linear_multiply(1.3)
|
||||||
|
} else {
|
||||||
|
BLUE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fill() -> Color32 {
|
pub fn fill() -> Color32 {
|
||||||
if use_dark() {
|
if use_dark() {
|
||||||
FILL_DARK
|
FILL_DARK
|
||||||
|
|
|
@ -143,6 +143,10 @@ impl PlatformCallbacks for Android {
|
||||||
&[JValue::Object(&JObject::from(arg_value))]).unwrap();
|
&[JValue::Object(&JObject::from(arg_value))]).unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pick_file(&self) -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
|
|
@ -131,6 +131,7 @@ impl PlatformCallbacks for Desktop {
|
||||||
|
|
||||||
fn share_data(&self, name: String, data: Vec<u8>) -> Result<(), std::io::Error> {
|
fn share_data(&self, name: String, data: Vec<u8>) -> Result<(), std::io::Error> {
|
||||||
let folder = FileDialog::new()
|
let folder = FileDialog::new()
|
||||||
|
.set_title(t!("share"))
|
||||||
.set_directory(dirs::home_dir().unwrap())
|
.set_directory(dirs::home_dir().unwrap())
|
||||||
.set_file_name(name.clone())
|
.set_file_name(name.clone())
|
||||||
.save_file();
|
.save_file();
|
||||||
|
@ -141,6 +142,17 @@ impl PlatformCallbacks for Desktop {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pick_file(&self) -> Option<String> {
|
||||||
|
let file = FileDialog::new()
|
||||||
|
.set_title(t!("choose_file"))
|
||||||
|
.set_directory(dirs::home_dir().unwrap())
|
||||||
|
.pick_file();
|
||||||
|
if let Some(file) = file {
|
||||||
|
return Some(file.to_str().unwrap_or_default().to_string());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
|
|
@ -32,4 +32,5 @@ pub trait PlatformCallbacks {
|
||||||
fn can_switch_camera(&self) -> bool;
|
fn can_switch_camera(&self) -> bool;
|
||||||
fn switch_camera(&self);
|
fn switch_camera(&self);
|
||||||
fn share_data(&self, name: String, data: Vec<u8>) -> Result<(), std::io::Error>;
|
fn share_data(&self, name: String, data: Vec<u8>) -> Result<(), std::io::Error>;
|
||||||
|
fn pick_file(&self) -> Option<String>;
|
||||||
}
|
}
|
95
src/gui/views/file.rs
Normal file
95
src/gui/views/file.rs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
// Copyright 2024 The Grim Developers
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::{fs, thread};
|
||||||
|
use parking_lot::RwLock;
|
||||||
|
|
||||||
|
use crate::gui::Colors;
|
||||||
|
use crate::gui::icons::FILE_ARROW_UP;
|
||||||
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
|
use crate::gui::views::View;
|
||||||
|
|
||||||
|
/// Button to pick file and parse its data into text.
|
||||||
|
pub struct FilePickButton {
|
||||||
|
/// Flag to check if file is parsing.
|
||||||
|
pub file_parsing: Arc<AtomicBool>,
|
||||||
|
/// File parsing result.
|
||||||
|
pub file_parsing_result: Arc<RwLock<Option<String>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FilePickButton {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
file_parsing: Arc::new(AtomicBool::new(false)),
|
||||||
|
file_parsing_result: Arc::new(RwLock::new(None))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilePickButton {
|
||||||
|
/// Draw button content.
|
||||||
|
pub fn ui(&mut self,
|
||||||
|
ui: &mut egui::Ui,
|
||||||
|
cb: &dyn PlatformCallbacks,
|
||||||
|
on_result: impl FnOnce(String)) {
|
||||||
|
if self.file_parsing.load(Ordering::Relaxed) {
|
||||||
|
// Draw loading spinner on file parsing.
|
||||||
|
View::small_loading_spinner(ui);
|
||||||
|
// Check file parsing result.
|
||||||
|
let has_result = {
|
||||||
|
let r_res = self.file_parsing_result.read();
|
||||||
|
r_res.is_some()
|
||||||
|
};
|
||||||
|
if has_result {
|
||||||
|
let text = {
|
||||||
|
let r_res = self.file_parsing_result.read();
|
||||||
|
r_res.clone().unwrap()
|
||||||
|
};
|
||||||
|
// Callback on result.
|
||||||
|
on_result(text);
|
||||||
|
// Clear result.
|
||||||
|
let mut w_res = self.file_parsing_result.write();
|
||||||
|
*w_res = None;
|
||||||
|
self.file_parsing.store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Draw button to pick file.
|
||||||
|
let file_text = format!("{} {}", FILE_ARROW_UP, t!("choose_file"));
|
||||||
|
View::colored_text_button(ui, file_text, Colors::blue(), Colors::button(), || {
|
||||||
|
if let Some(path) = cb.pick_file() {
|
||||||
|
// Parse file at new thread.
|
||||||
|
self.file_parsing.store(true, Ordering::Relaxed);
|
||||||
|
let result = self.file_parsing_result.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
if path.ends_with(".gif") {
|
||||||
|
//TODO: Detect QR codes on GIF file.
|
||||||
|
} else if path.ends_with(".jpeg") || path.ends_with(".jpg") ||
|
||||||
|
path.ends_with(".png") {
|
||||||
|
//TODO: Detect QR codes on image files.
|
||||||
|
} else {
|
||||||
|
// Parse file as plain text.
|
||||||
|
if let Ok(text) = fs::read_to_string(path) {
|
||||||
|
let mut w_res = result.write();
|
||||||
|
*w_res = Some(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -36,4 +36,7 @@ mod camera;
|
||||||
pub use camera::*;
|
pub use camera::*;
|
||||||
|
|
||||||
mod qr;
|
mod qr;
|
||||||
pub use qr::*;
|
pub use qr::*;
|
||||||
|
|
||||||
|
mod file;
|
||||||
|
pub use file::*;
|
|
@ -43,7 +43,7 @@ pub struct Modal {
|
||||||
|
|
||||||
impl Modal {
|
impl Modal {
|
||||||
/// Margin from [`Modal`] window at top/left/right.
|
/// Margin from [`Modal`] window at top/left/right.
|
||||||
const DEFAULT_MARGIN: f32 = 8.0;
|
const DEFAULT_MARGIN: f32 = 4.0;
|
||||||
/// Maximum width of the content.
|
/// Maximum width of the content.
|
||||||
const DEFAULT_WIDTH: f32 = Root::SIDE_PANEL_WIDTH - (2.0 * Self::DEFAULT_MARGIN);
|
const DEFAULT_WIDTH: f32 = Root::SIDE_PANEL_WIDTH - (2.0 * Self::DEFAULT_MARGIN);
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ use parking_lot::RwLock;
|
||||||
use crate::gui::Colors;
|
use crate::gui::Colors;
|
||||||
use crate::gui::icons::{BROOM, CLIPBOARD_TEXT, COPY, DOWNLOAD_SIMPLE, PROHIBIT, QR_CODE, SCAN, UPLOAD_SIMPLE};
|
use crate::gui::icons::{BROOM, CLIPBOARD_TEXT, COPY, DOWNLOAD_SIMPLE, PROHIBIT, QR_CODE, SCAN, UPLOAD_SIMPLE};
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{CameraContent, Modal, QrCodeContent, Root, View};
|
use crate::gui::views::{CameraContent, FilePickButton, Modal, QrCodeContent, Root, View};
|
||||||
use crate::gui::views::types::{ModalPosition, QrScanResult, TextEditOptions};
|
use crate::gui::views::types::{ModalPosition, QrScanResult, TextEditOptions};
|
||||||
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;
|
||||||
|
@ -72,6 +72,8 @@ pub struct WalletMessages {
|
||||||
response_edit: String,
|
response_edit: String,
|
||||||
/// Flag to check if Dandelion is needed to finalize transaction.
|
/// Flag to check if Dandelion is needed to finalize transaction.
|
||||||
dandelion: bool,
|
dandelion: bool,
|
||||||
|
/// Button to parse picked file content.
|
||||||
|
file_pick_button: FilePickButton,
|
||||||
|
|
||||||
/// Flag to check if invoice or sending request was opened for [`Modal`].
|
/// Flag to check if invoice or sending request was opened for [`Modal`].
|
||||||
request_invoice: bool,
|
request_invoice: bool,
|
||||||
|
@ -168,6 +170,7 @@ impl WalletMessages {
|
||||||
message_error: None,
|
message_error: None,
|
||||||
response_edit: "".to_string(),
|
response_edit: "".to_string(),
|
||||||
dandelion,
|
dandelion,
|
||||||
|
file_pick_button: FilePickButton::default(),
|
||||||
request_amount_edit: "".to_string(),
|
request_amount_edit: "".to_string(),
|
||||||
request_edit: "".to_string(),
|
request_edit: "".to_string(),
|
||||||
request_error: None,
|
request_error: None,
|
||||||
|
@ -795,7 +798,9 @@ impl WalletMessages {
|
||||||
|
|
||||||
ui.add_space(10.0);
|
ui.add_space(10.0);
|
||||||
|
|
||||||
// Draw clear button on message input or cancel and clear buttons on response.
|
// Draw clear button on message input,
|
||||||
|
// cancel and clear buttons on response
|
||||||
|
// or button to choose text or image file.
|
||||||
if !self.message_loading {
|
if !self.message_loading {
|
||||||
if self.message_slate.is_none() && !self.message_edit.is_empty() {
|
if self.message_slate.is_none() && !self.message_edit.is_empty() {
|
||||||
// Draw button to clear message input.
|
// Draw button to clear message input.
|
||||||
|
@ -808,8 +813,8 @@ impl WalletMessages {
|
||||||
});
|
});
|
||||||
} else if !self.response_edit.is_empty() && self.message_slate.is_some() {
|
} else if !self.response_edit.is_empty() && self.message_slate.is_some() {
|
||||||
// Draw cancel button.
|
// Draw cancel button.
|
||||||
let cancel = format!("{} {}", PROHIBIT, t!("modal.cancel"));
|
let cancel_text = format!("{} {}", PROHIBIT, t!("modal.cancel"));
|
||||||
View::colored_text_button(ui, cancel, Colors::red(), Colors::button(), || {
|
View::colored_text_button(ui, cancel_text, Colors::red(), Colors::button(), || {
|
||||||
let slate = self.message_slate.clone().unwrap();
|
let slate = self.message_slate.clone().unwrap();
|
||||||
if let Some(tx) = wallet.tx_by_slate(&slate) {
|
if let Some(tx) = wallet.tx_by_slate(&slate) {
|
||||||
wallet.cancel(tx.data.id);
|
wallet.cancel(tx.data.id);
|
||||||
|
@ -818,6 +823,17 @@ impl WalletMessages {
|
||||||
self.message_slate = None;
|
self.message_slate = None;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// Draw button to choose file.
|
||||||
|
let mut parsed_text = "".to_string();
|
||||||
|
self.file_pick_button.ui(ui, cb, |text| {
|
||||||
|
parsed_text = text;
|
||||||
|
});
|
||||||
|
if !parsed_text.is_empty() {
|
||||||
|
// Parse Slatepack message from file content.
|
||||||
|
self.message_edit = parsed_text;
|
||||||
|
self.parse_message(wallet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,9 +24,9 @@ use grin_wallet_libwallet::{Error, Slate, SlateState, TxLogEntryType};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
|
||||||
use crate::gui::Colors;
|
use crate::gui::Colors;
|
||||||
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, QR_CODE, SCAN, X_CIRCLE};
|
use crate::gui::icons::{ARROW_CIRCLE_DOWN, ARROW_CIRCLE_UP, ARROW_CLOCKWISE, BRIDGE, BROOM, CALENDAR_CHECK, CHAT_CIRCLE_TEXT, CHECK, CHECK_CIRCLE, CLIPBOARD_TEXT, COPY, DOTS_THREE_CIRCLE, FILE_ARCHIVE, FILE_TEXT, GEAR_FINE, HASH_STRAIGHT, PROHIBIT, QR_CODE, SCAN, X_CIRCLE};
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{CameraContent, Modal, QrCodeContent, Root, View};
|
use crate::gui::views::{CameraContent, FilePickButton, Modal, QrCodeContent, Root, View};
|
||||||
use crate::gui::views::types::ModalPosition;
|
use crate::gui::views::types::ModalPosition;
|
||||||
use crate::gui::views::wallets::types::WalletTab;
|
use crate::gui::views::wallets::types::WalletTab;
|
||||||
use crate::gui::views::wallets::wallet::types::{GRIN, SLATEPACK_MESSAGE_HINT, WalletTabType};
|
use crate::gui::views::wallets::wallet::types::{GRIN, SLATEPACK_MESSAGE_HINT, WalletTabType};
|
||||||
|
@ -60,6 +60,8 @@ pub struct WalletTransactions {
|
||||||
tx_info_show_scanner: bool,
|
tx_info_show_scanner: bool,
|
||||||
/// QR code scanner [`Modal`] content.
|
/// QR code scanner [`Modal`] content.
|
||||||
tx_info_scanner_content: CameraContent,
|
tx_info_scanner_content: CameraContent,
|
||||||
|
/// Button to parse picked file content at [`Modal`].
|
||||||
|
tx_info_file_pick_button: FilePickButton,
|
||||||
|
|
||||||
/// Transaction identifier to use at confirmation [`Modal`].
|
/// Transaction identifier to use at confirmation [`Modal`].
|
||||||
confirm_cancel_tx_id: Option<u32>,
|
confirm_cancel_tx_id: Option<u32>,
|
||||||
|
@ -83,6 +85,7 @@ impl Default for WalletTransactions {
|
||||||
tx_info_qr_code_content: QrCodeContent::new("".to_string(), true),
|
tx_info_qr_code_content: QrCodeContent::new("".to_string(), true),
|
||||||
tx_info_show_scanner: false,
|
tx_info_show_scanner: false,
|
||||||
tx_info_scanner_content: CameraContent::default(),
|
tx_info_scanner_content: CameraContent::default(),
|
||||||
|
tx_info_file_pick_button: FilePickButton::default(),
|
||||||
confirm_cancel_tx_id: None,
|
confirm_cancel_tx_id: None,
|
||||||
manual_sync: None,
|
manual_sync: None,
|
||||||
}
|
}
|
||||||
|
@ -146,8 +149,6 @@ impl WalletTransactions {
|
||||||
let amount_conf = data.info.amount_awaiting_confirmation;
|
let amount_conf = data.info.amount_awaiting_confirmation;
|
||||||
let amount_fin = data.info.amount_awaiting_finalization;
|
let amount_fin = data.info.amount_awaiting_finalization;
|
||||||
let amount_locked = data.info.amount_locked;
|
let amount_locked = data.info.amount_locked;
|
||||||
|
|
||||||
// Show transactions info.
|
|
||||||
View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| {
|
View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| {
|
||||||
// Show non-zero awaiting confirmation amount.
|
// Show non-zero awaiting confirmation amount.
|
||||||
if amount_conf != 0 {
|
if amount_conf != 0 {
|
||||||
|
@ -228,10 +229,10 @@ impl WalletTransactions {
|
||||||
View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| {
|
View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| {
|
||||||
let padding = amount_conf != 0 || amount_fin != 0 || amount_locked != 0;
|
let padding = amount_conf != 0 || amount_fin != 0 || amount_locked != 0;
|
||||||
for index in row_range {
|
for index in row_range {
|
||||||
// Show transaction item.
|
|
||||||
let tx = txs.get(index).unwrap();
|
let tx = txs.get(index).unwrap();
|
||||||
let rounding = View::item_rounding(index, txs.len(), false);
|
let r = View::item_rounding(index, txs.len(), false);
|
||||||
self.tx_item_ui(ui, tx, rounding, padding, true, &data, wallet);
|
let show_info = tx.data.tx_slate_id.is_some();
|
||||||
|
self.tx_item_ui(ui, tx, r, padding, show_info, &data, wallet);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
@ -318,7 +319,7 @@ impl WalletTransactions {
|
||||||
|
|
||||||
ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| {
|
ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| {
|
||||||
// Draw button to show transaction info.
|
// Draw button to show transaction info.
|
||||||
if can_show_info && tx.data.tx_slate_id.is_some() {
|
if can_show_info && tx.from_node {
|
||||||
rounding.nw = 0.0;
|
rounding.nw = 0.0;
|
||||||
rounding.sw = 0.0;
|
rounding.sw = 0.0;
|
||||||
View::item_button(ui, rounding, FILE_TEXT, None, || {
|
View::item_button(ui, rounding, FILE_TEXT, None, || {
|
||||||
|
@ -357,7 +358,7 @@ impl WalletTransactions {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw cancel button for tx that can be reposted and canceled.
|
// Draw cancel button for tx that can be reposted and canceled.
|
||||||
let wallet_loaded = wallet.foreign_api_port().is_some();
|
let wallet_loaded = tx.from_node && wallet.foreign_api_port().is_some();
|
||||||
if wallet_loaded && ((!can_show_info && !self.tx_info_finalizing) || can_show_info) &&
|
if wallet_loaded && ((!can_show_info && !self.tx_info_finalizing) || can_show_info) &&
|
||||||
(tx.can_repost(data) || tx.can_cancel()) {
|
(tx.can_repost(data) || tx.can_cancel()) {
|
||||||
View::item_button(ui, Rounding::default(), PROHIBIT, Some(Colors::red()), || {
|
View::item_button(ui, Rounding::default(), PROHIBIT, Some(Colors::red()), || {
|
||||||
|
@ -828,7 +829,7 @@ impl WalletTransactions {
|
||||||
|
|
||||||
ui.add_space(2.0);
|
ui.add_space(2.0);
|
||||||
View::horizontal_line(ui, Colors::item_stroke());
|
View::horizontal_line(ui, Colors::item_stroke());
|
||||||
ui.add_space(8.0);
|
ui.add_space(10.0);
|
||||||
|
|
||||||
// Do not show buttons on finalization.
|
// Do not show buttons on finalization.
|
||||||
if self.tx_info_finalizing {
|
if self.tx_info_finalizing {
|
||||||
|
@ -862,6 +863,28 @@ impl WalletTransactions {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
ui.add_space(8.0);
|
||||||
|
ui.vertical_centered(|ui| {
|
||||||
|
if self.tx_info_finalize_error {
|
||||||
|
// Draw button to clear message input.
|
||||||
|
let clear_text = format!("{} {}", BROOM, t!("clear"));
|
||||||
|
View::button(ui, clear_text, Colors::button(), || {
|
||||||
|
self.tx_info_finalize_edit.clear();
|
||||||
|
self.tx_info_finalize_error = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Draw button to choose file.
|
||||||
|
let mut parsed_text = "".to_string();
|
||||||
|
self.tx_info_file_pick_button.ui(ui, cb, |text| {
|
||||||
|
parsed_text = text;
|
||||||
|
});
|
||||||
|
if !parsed_text.is_empty() {
|
||||||
|
// Parse Slatepack message from file content.
|
||||||
|
self.tx_info_finalize_edit = parsed_text;
|
||||||
|
self.on_finalization_input_change(tx, wallet, modal, cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
ui.columns(2, |columns| {
|
ui.columns(2, |columns| {
|
||||||
columns[0].vertical_centered_justified(|ui| {
|
columns[0].vertical_centered_justified(|ui| {
|
||||||
|
|
|
@ -156,13 +156,15 @@ pub struct WalletTransaction {
|
||||||
/// Block height when tx was confirmed.
|
/// Block height when tx was confirmed.
|
||||||
pub conf_height: Option<u64>,
|
pub conf_height: Option<u64>,
|
||||||
/// Block height when tx was reposted.
|
/// Block height when tx was reposted.
|
||||||
pub repost_height: Option<u64>
|
pub repost_height: Option<u64>,
|
||||||
|
/// Flag to check if tx was received after sync from node.
|
||||||
|
pub from_node: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WalletTransaction {
|
impl WalletTransaction {
|
||||||
/// Check if transaction can be cancelled.
|
/// Check if transaction can be cancelled.
|
||||||
pub fn can_cancel(&self) -> bool {
|
pub fn can_cancel(&self) -> bool {
|
||||||
!self.cancelling && !self.posting && !self.data.confirmed &&
|
self.from_node && !self.cancelling && !self.posting && !self.data.confirmed &&
|
||||||
self.data.tx_type != TxLogEntryType::TxReceivedCancelled
|
self.data.tx_type != TxLogEntryType::TxReceivedCancelled
|
||||||
&& self.data.tx_type != TxLogEntryType::TxSentCancelled
|
&& self.data.tx_type != TxLogEntryType::TxSentCancelled
|
||||||
}
|
}
|
||||||
|
@ -171,7 +173,7 @@ impl WalletTransaction {
|
||||||
pub fn can_repost(&self, data: &WalletData) -> bool {
|
pub fn can_repost(&self, data: &WalletData) -> bool {
|
||||||
let last_height = data.info.last_confirmed_height;
|
let last_height = data.info.last_confirmed_height;
|
||||||
let min_conf = data.info.minimum_confirmations;
|
let min_conf = data.info.minimum_confirmations;
|
||||||
self.posting && self.repost_height.is_some() &&
|
self.from_node && self.posting && self.repost_height.is_some() &&
|
||||||
last_height - self.repost_height.unwrap() > min_conf
|
last_height - self.repost_height.unwrap() > min_conf
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -584,9 +584,9 @@ impl Wallet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse Slatepack message into [`Slate`].
|
/// Parse Slatepack message into [`Slate`].
|
||||||
pub fn parse_slatepack(&self, message: &String) -> Result<Slate, grin_wallet_controller::Error> {
|
pub fn parse_slatepack(&self, text: &String) -> Result<Slate, grin_wallet_controller::Error> {
|
||||||
let mut api = Owner::new(self.instance.clone().unwrap(), None);
|
let mut api = Owner::new(self.instance.clone().unwrap(), None);
|
||||||
return match parse_slatepack(&mut api, None, None, Some(message.clone())) {
|
return match parse_slatepack(&mut api, None, None, Some(text.clone())) {
|
||||||
Ok(s) => Ok(s.0),
|
Ok(s) => Ok(s.0),
|
||||||
Err(e) => Err(e)
|
Err(e) => Err(e)
|
||||||
}
|
}
|
||||||
|
@ -1168,7 +1168,9 @@ fn start_sync(wallet: Wallet) -> Thread {
|
||||||
let failed_sync = wallet.sync_error() || wallet.get_sync_attempts() != 0;
|
let failed_sync = wallet.sync_error() || wallet.get_sync_attempts() != 0;
|
||||||
|
|
||||||
// Clear syncing status.
|
// Clear syncing status.
|
||||||
wallet.syncing.store(false, Ordering::Relaxed);
|
if !failed_sync {
|
||||||
|
wallet.syncing.store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
// Repeat after default or attempt delay if synchronization was not successful.
|
// Repeat after default or attempt delay if synchronization was not successful.
|
||||||
let delay = if failed_sync {
|
let delay = if failed_sync {
|
||||||
|
@ -1288,12 +1290,12 @@ fn sync_wallet_data(wallet: &Wallet, from_node: bool) {
|
||||||
tx.amount_credited - tx.amount_debited
|
tx.amount_credited - tx.amount_debited
|
||||||
};
|
};
|
||||||
|
|
||||||
let unconfirmed_sent_or_received = tx.tx_slate_id.is_some() &&
|
let unc_sent_or_received = tx.tx_slate_id.is_some() &&
|
||||||
!tx.confirmed && (tx.tx_type == TxLogEntryType::TxSent ||
|
!tx.confirmed && (tx.tx_type == TxLogEntryType::TxSent ||
|
||||||
tx.tx_type == TxLogEntryType::TxReceived);
|
tx.tx_type == TxLogEntryType::TxReceived);
|
||||||
|
|
||||||
// Setup transaction posting status based on slate state.
|
// Setup transaction posting status based on slate state.
|
||||||
let posting = if unconfirmed_sent_or_received {
|
let posting = if unc_sent_or_received {
|
||||||
// Create slate to check existing file.
|
// Create slate to check existing file.
|
||||||
let is_invoice = tx.tx_type == TxLogEntryType::TxReceived;
|
let is_invoice = tx.tx_type == TxLogEntryType::TxReceived;
|
||||||
let mut slate = Slate::blank(0, is_invoice);
|
let mut slate = Slate::blank(0, is_invoice);
|
||||||
|
@ -1322,8 +1324,8 @@ fn sync_wallet_data(wallet: &Wallet, from_node: bool) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Setup flag for ability to finalize transaction.
|
// Setup flag for ability to finalize transaction.
|
||||||
let can_finalize = if !posting && unconfirmed_sent_or_received {
|
let can_finalize = if from_node && !posting && unc_sent_or_received {
|
||||||
// Create slate to check existing file.
|
// Check existing file.
|
||||||
let mut slate = Slate::blank(1, false);
|
let mut slate = Slate::blank(1, false);
|
||||||
slate.id = tx.tx_slate_id.unwrap();
|
slate.id = tx.tx_slate_id.unwrap();
|
||||||
slate.state = match tx.tx_type {
|
slate.state = match tx.tx_type {
|
||||||
|
@ -1400,7 +1402,8 @@ fn sync_wallet_data(wallet: &Wallet, from_node: bool) {
|
||||||
posting,
|
posting,
|
||||||
can_finalize,
|
can_finalize,
|
||||||
conf_height,
|
conf_height,
|
||||||
repost_height
|
repost_height,
|
||||||
|
from_node
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue