feat: calculate fee and maximum amount on send
Reviewed-on: https://code.gri.mw/GUI/grim/pulls/32
This commit is contained in:
Generated
+96
-9
@@ -70,7 +70,7 @@ checksum = "301e55b39cfc15d9c48943ce5f572204a551646700d0e8efa424585f94fec528"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_atspi_common",
|
||||
"async-channel",
|
||||
"async-channel 2.5.0",
|
||||
"async-executor",
|
||||
"async-task",
|
||||
"atspi",
|
||||
@@ -596,12 +596,23 @@ version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
"event-listener 5.4.1",
|
||||
"event-listener-strategy",
|
||||
"futures-core",
|
||||
"pin-project-lite 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"event-listener 2.5.3",
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
version = "2.5.0"
|
||||
@@ -652,6 +663,21 @@ dependencies = [
|
||||
"futures-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-global-executor"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c"
|
||||
dependencies = [
|
||||
"async-channel 2.5.0",
|
||||
"async-executor",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"blocking",
|
||||
"futures-lite",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-io"
|
||||
version = "2.6.0"
|
||||
@@ -676,7 +702,7 @@ version = "3.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
"event-listener 5.4.1",
|
||||
"event-listener-strategy",
|
||||
"pin-project-lite 0.2.16",
|
||||
]
|
||||
@@ -710,14 +736,14 @@ version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-channel 2.5.0",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"async-signal",
|
||||
"async-task",
|
||||
"blocking",
|
||||
"cfg-if 1.0.4",
|
||||
"event-listener",
|
||||
"event-listener 5.4.1",
|
||||
"futures-lite",
|
||||
"rustix 1.1.2",
|
||||
]
|
||||
@@ -761,6 +787,32 @@ dependencies = [
|
||||
"tokio 1.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-std"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b"
|
||||
dependencies = [
|
||||
"async-channel 1.9.0",
|
||||
"async-global-executor",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"crossbeam-utils",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-lite",
|
||||
"gloo-timers",
|
||||
"kv-log-macro",
|
||||
"log",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"pin-project-lite 0.2.16",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
"wasm-bindgen-futures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stream"
|
||||
version = "0.3.6"
|
||||
@@ -1232,7 +1284,7 @@ version = "1.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-channel 2.5.0",
|
||||
"async-task",
|
||||
"futures-io",
|
||||
"futures-lite",
|
||||
@@ -2974,6 +3026,12 @@ dependencies = [
|
||||
"num-traits 0.2.19",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "5.4.1"
|
||||
@@ -2991,7 +3049,7 @@ version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
"event-listener 5.4.1",
|
||||
"pin-project-lite 0.2.16",
|
||||
]
|
||||
|
||||
@@ -3652,6 +3710,18 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glow"
|
||||
version = "0.16.0"
|
||||
@@ -3790,6 +3860,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"arboard",
|
||||
"arti-client",
|
||||
"async-std",
|
||||
"backtrace",
|
||||
"bytes 1.10.1",
|
||||
"chrono",
|
||||
@@ -5413,6 +5484,15 @@ dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kv-log-macro"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
@@ -5620,6 +5700,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"value-bag",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10291,7 +10372,7 @@ dependencies = [
|
||||
"derive_more",
|
||||
"digest 0.10.7",
|
||||
"educe",
|
||||
"event-listener",
|
||||
"event-listener 5.4.1",
|
||||
"fs-mistrust",
|
||||
"fslock",
|
||||
"futures 0.3.31",
|
||||
@@ -11507,6 +11588,12 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "value-bag"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
@@ -12836,7 +12923,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"blocking",
|
||||
"enumflags2",
|
||||
"event-listener",
|
||||
"event-listener 5.4.1",
|
||||
"futures-core",
|
||||
"futures-lite",
|
||||
"hex",
|
||||
|
||||
@@ -118,6 +118,7 @@ bytes = "1.10.1"
|
||||
hyper-socks2 = "0.9.1"
|
||||
hyper-proxy2 = "0.1.0"
|
||||
hyper-tls = "0.6.0"
|
||||
async-std = "1.13.2"
|
||||
|
||||
## tor
|
||||
arti-client = { version = "0.36.0", features = ["pt-client", "static", "onion-service-service", "onion-service-client"] }
|
||||
|
||||
@@ -32,6 +32,7 @@ crash_report: Absturzbericht
|
||||
crash_report_warning: Anwendung wurde beim letzten Mal unerwartet geschlossen, Sie können den Absturzbericht mit Entwicklern teilen.
|
||||
confirmation: Bestätigung
|
||||
enter_url: URL eingeben
|
||||
max_short: MAX
|
||||
wallets:
|
||||
await_conf_amount: Erwarte Bestätigung
|
||||
await_fin_amount: Warten auf die Fertigstellung
|
||||
@@ -130,6 +131,7 @@ wallets:
|
||||
tx_receive_cancel_conf: 'Sind Sie sicher, dass Sie das Empfangen von %{amount} ツ abbrechen wollen?'
|
||||
rec_phrase_not_found: Wiederhestellungsphrase nicht gefunden.
|
||||
restore_wallet_desc: Stellen Sie das Wallet wieder her, indem Sie alle Dateien löschen. Wenn die normale Reparatur nicht geholfen hat, müssen Sie Ihr Wallet erneut öffnen.
|
||||
fee_base_desc: 'Gebühr (basiswert%{value}):'
|
||||
transport:
|
||||
desc: 'Transport verwenden, um Nachrichten synchron zu empfangen oder zu senden:'
|
||||
tor_network: Tor Netzwek
|
||||
|
||||
@@ -32,6 +32,7 @@ crash_report: Crash report
|
||||
crash_report_warning: Application closed unexpectedly last time, you can share crash report with developers.
|
||||
confirmation: Confirmation
|
||||
enter_url: Enter URL
|
||||
max_short: MAX
|
||||
wallets:
|
||||
await_conf_amount: Awaiting confirmation
|
||||
await_fin_amount: Awaiting finalization
|
||||
@@ -130,6 +131,7 @@ wallets:
|
||||
tx_receive_cancel_conf: 'Are you sure you want to cancel receiving of %{amount} ツ?'
|
||||
rec_phrase_not_found: Recovery phrase not found.
|
||||
restore_wallet_desc: Restore wallet by deleting all files if usual repair not helped, you will need to re-open your wallet.
|
||||
fee_base_desc: 'Fee (base value%{value}):'
|
||||
transport:
|
||||
desc: 'Use transport to receive or send messages synchronously:'
|
||||
tor_network: Tor network
|
||||
|
||||
@@ -32,6 +32,7 @@ crash_report: Rapport d'échec
|
||||
crash_report_warning: L'application s'est fermée de manière inattendue la dernière fois, vous pouvez partager un rapport d'incident avec les développeurs.
|
||||
confirmation: Confirmation
|
||||
enter_url: Entrez l'URL
|
||||
max_short: MAX
|
||||
wallets:
|
||||
await_conf_amount: En attente de confirmation
|
||||
await_fin_amount: En attente de finalisation
|
||||
@@ -130,6 +131,7 @@ wallets:
|
||||
tx_receive_cancel_conf: 'Êtes-vous sûr de vouloir annuler la réception de %{amount} ツ?'
|
||||
rec_phrase_not_found: Phrase de récupération non trouvée.
|
||||
restore_wallet_desc: "Restaurer le portefeuille en supprimant tous les fichiers si la réparation habituelle n'a pas aidé. Vous devrez rouvrir votre portefeuille."
|
||||
fee_base_desc: 'Frais (valeur de base%{value}):'
|
||||
transport:
|
||||
desc: 'Utilisez le transport pour recevoir ou envoyer des messages de manière synchronisée:'
|
||||
tor_network: Réseau Tor
|
||||
|
||||
@@ -32,6 +32,7 @@ crash_report: Отчёт о сбое
|
||||
crash_report_warning: В прошлый раз приложение неожиданно закрылось, вы можете поделиться отчетом о сбое с разработчиками.
|
||||
confirmation: Подтверждение
|
||||
enter_url: Введите URL-адрес
|
||||
max_short: МАКС
|
||||
wallets:
|
||||
await_conf_amount: Ожидает подтверждения
|
||||
await_fin_amount: Ожидает завершения
|
||||
@@ -130,6 +131,7 @@ wallets:
|
||||
tx_receive_cancel_conf: 'Вы действительно хотите отменить получение %{amount} ツ?'
|
||||
rec_phrase_not_found: Фраза восстановления не найдена.
|
||||
restore_wallet_desc: Восстановить кошелёк, удалив все файлы, если обычное исправление не помогло. Необходимо переоткрыть кошелёк.
|
||||
fee_base_desc: 'Комиссия (базовое значение%{value}):'
|
||||
transport:
|
||||
desc: 'Используйте транспорт для синхронных получения или отправки сообщений:'
|
||||
tor_network: Сеть Tor
|
||||
|
||||
@@ -32,6 +32,7 @@ crash_report: Ariza Raporu
|
||||
crash_report_warning: Uygulama beklenmedik bir sekilde kapandi son kez, kilitlenme raporunu gelistiricilerle paylasabilirsiniz.
|
||||
confirmation: Onay
|
||||
enter_url: URL'yi girin
|
||||
max_short: MAKS
|
||||
wallets:
|
||||
await_conf_amount: Onay bekleniyor
|
||||
await_fin_amount: Tamamlanma bekleniyor
|
||||
@@ -130,6 +131,7 @@ wallets:
|
||||
tx_receive_cancel_conf: Gelen tx iptal
|
||||
rec_phrase_not_found: Sifre kelime bulunmuyor
|
||||
restore_wallet_desc: Cuzdani restore et
|
||||
fee_base_desc: 'Ücret (taban değeri%{value}):'
|
||||
transport:
|
||||
desc: 'Adresten senkronize GONDER veya AL:'
|
||||
tor_network: Tor network
|
||||
|
||||
@@ -32,6 +32,7 @@ crash_report: 崩溃报告
|
||||
crash_report_warning: 上次应用程序意外关闭,您可以报告开发人员崩溃事件.
|
||||
confirmation: 确认
|
||||
enter_url: 输入 URL
|
||||
max_short: 最大數量
|
||||
wallets:
|
||||
await_conf_amount: 等待确认中
|
||||
await_fin_amount: 等待确定中
|
||||
@@ -131,6 +132,7 @@ wallets:
|
||||
tx_receive_cancel_conf: '您确定要取消 %{amount} ツ的接收吗?'
|
||||
rec_phrase_not_found: 找不到恢复助记词.
|
||||
restore_wallet_desc: 如果常规修复没有帮助,通过删除所有文件来恢复钱包.您将需要重新打开您的钱包.
|
||||
fee_base_desc: '费用 (基值%{value}):'
|
||||
transport:
|
||||
desc: '使用传输同步接收或发送消息:'
|
||||
tor_network: Tor 网络
|
||||
|
||||
@@ -12,22 +12,24 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::sync::Arc;
|
||||
use egui::{Layout, TextBuffer, TextStyle, Widget, Align, ViewportCommand};
|
||||
use egui::text_edit::TextEditState;
|
||||
use egui::{Align, Layout, TextBuffer, TextStyle, ViewportCommand, Widget};
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{CLIPBOARD_TEXT, COPY, EYE, EYE_SLASH, SCAN};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::input::keyboard::KeyboardContent;
|
||||
use crate::gui::views::{KeyboardEvent, View};
|
||||
use crate::gui::Colors;
|
||||
|
||||
/// Text input content.
|
||||
pub struct TextEdit {
|
||||
/// View identifier.
|
||||
id: egui::Id,
|
||||
/// Check if input is enabled or disabled.
|
||||
enabled: bool,
|
||||
/// Horizontal text centering is needed.
|
||||
h_center: bool,
|
||||
/// Focus is needed.
|
||||
@@ -59,6 +61,7 @@ impl TextEdit {
|
||||
pub fn new(id: egui::Id) -> Self {
|
||||
Self {
|
||||
id,
|
||||
enabled: true,
|
||||
h_center: false,
|
||||
focus: true,
|
||||
focus_request: false,
|
||||
@@ -73,8 +76,26 @@ impl TextEdit {
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw text input content.
|
||||
/// Draw text input.
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui, input: &mut String, cb: &dyn PlatformCallbacks) {
|
||||
self.input_ui(ui, input, |_| {}, cb);
|
||||
}
|
||||
|
||||
/// Draw text input with additional buttons (right to left order).
|
||||
pub fn custom_buttons_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
input: &mut String,
|
||||
cb: &dyn PlatformCallbacks,
|
||||
buttons_content: impl FnOnce(&mut egui::Ui)) {
|
||||
self.input_ui(ui, input, buttons_content, cb);
|
||||
}
|
||||
|
||||
/// Draw text input content.
|
||||
fn input_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
input: &mut String,
|
||||
buttons_content: impl FnOnce(&mut egui::Ui),
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
let mut layout_rect = ui.available_rect_before_wrap();
|
||||
layout_rect.set_height(Self::TEXT_EDIT_HEIGHT);
|
||||
ui.allocate_ui_with_layout(layout_rect.size(), Layout::right_to_left(Align::Max), |ui| {
|
||||
@@ -95,6 +116,9 @@ impl TextEdit {
|
||||
ui.add_space(8.0);
|
||||
}
|
||||
|
||||
// Extra buttons content.
|
||||
(buttons_content)(ui);
|
||||
|
||||
// Setup copy button.
|
||||
if self.copy {
|
||||
let copy_icon = COPY.to_string();
|
||||
@@ -137,6 +161,12 @@ impl TextEdit {
|
||||
|
||||
// Show text edit.
|
||||
let text_edit_resp = egui::TextEdit::singleline(input)
|
||||
.text_color(if self.enabled {
|
||||
Colors::text(false)
|
||||
} else {
|
||||
Colors::inactive_text()
|
||||
})
|
||||
.interactive(self.enabled)
|
||||
.id(self.id)
|
||||
.font(TextStyle::Heading)
|
||||
.min_size(edit_rect.size())
|
||||
@@ -181,10 +211,8 @@ impl TextEdit {
|
||||
}
|
||||
});
|
||||
});
|
||||
// Repaint on Android to handle input from Java code without delays.
|
||||
if is_android() {
|
||||
ui.ctx().request_repaint();
|
||||
}
|
||||
// Immediate repaint when input is open.
|
||||
ui.ctx().request_repaint();
|
||||
}
|
||||
|
||||
/// Apply soft keyboard input data to provided String, returns `true` if Enter was pressed.
|
||||
@@ -283,6 +311,27 @@ impl TextEdit {
|
||||
false
|
||||
}
|
||||
|
||||
/// Set cursor to the end of text.
|
||||
pub fn cursor_to_end(&self, text_len: usize, ui: &mut egui::Ui) {
|
||||
let mut state = TextEditState::load(ui.ctx(), self.id).unwrap();
|
||||
match state.cursor.char_range() {
|
||||
None => {}
|
||||
Some(range) => {
|
||||
let mut r = range.clone();
|
||||
r.primary.index = text_len;
|
||||
r.secondary.index = text_len;
|
||||
state.cursor.set_char_range(Some(r));
|
||||
TextEditState::store(state, ui.ctx(), self.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable input.
|
||||
pub fn disable(mut self) -> Self {
|
||||
self.enabled = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Center text horizontally.
|
||||
pub fn h_center(mut self) -> Self {
|
||||
self.h_center = true;
|
||||
|
||||
@@ -460,12 +460,17 @@ impl View {
|
||||
});
|
||||
}
|
||||
|
||||
/// Draw loading spinner.
|
||||
pub fn loading_spinner(ui: &mut egui::Ui, size: f32) {
|
||||
Spinner::new().size(size).color(Colors::gold()).ui(ui);
|
||||
}
|
||||
|
||||
/// Size of big loading spinner.
|
||||
pub const BIG_SPINNER_SIZE: f32 = 104.0;
|
||||
|
||||
/// Draw big gold loading spinner.
|
||||
pub fn big_loading_spinner(ui: &mut egui::Ui) {
|
||||
Spinner::new().size(Self::BIG_SPINNER_SIZE).color(Colors::gold()).ui(ui);
|
||||
View::loading_spinner(ui, View::BIG_SPINNER_SIZE);
|
||||
}
|
||||
|
||||
/// Size of big loading spinner.
|
||||
@@ -473,7 +478,7 @@ impl View {
|
||||
|
||||
/// Draw small gold loading spinner.
|
||||
pub fn small_loading_spinner(ui: &mut egui::Ui) {
|
||||
Spinner::new().size(30.0).color(Colors::gold()).ui(ui);
|
||||
View::loading_spinner(ui, View::SMALL_SPINNER_SIZE);
|
||||
}
|
||||
|
||||
/// Draw the button that looks like checkbox with callback on check.
|
||||
|
||||
@@ -130,11 +130,16 @@ impl ContentContainer for WalletsContent {
|
||||
}
|
||||
|
||||
fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
|
||||
ui.ctx().request_repaint_after(if OperatingSystem::from_target_os() == OperatingSystem::Android {
|
||||
Duration::from_millis(100)
|
||||
let is_android = OperatingSystem::from_target_os() == OperatingSystem::Android;
|
||||
let account_list_showing = self.wallet_content.account_content.list_content.is_some();
|
||||
// Small repaint delay is needed for Android back navigation and account list opening.
|
||||
ui.ctx().request_repaint_after(Duration::from_millis(if account_list_showing {
|
||||
10
|
||||
} else if is_android {
|
||||
100
|
||||
} else {
|
||||
Duration::from_millis(1000)
|
||||
});
|
||||
1000
|
||||
}));
|
||||
|
||||
if let Some(data) = crate::consume_incoming_data() {
|
||||
if !data.is_empty() {
|
||||
|
||||
@@ -19,13 +19,11 @@ use grin_chain::SyncStatus;
|
||||
use crate::gui::icons::{ARROWS_CLOCKWISE, FILE_ARROW_DOWN, FILE_ARROW_UP, GEAR_FINE, POWER, STACK};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::types::{LinePosition, ModalPosition};
|
||||
use crate::gui::views::wallets::types::{WalletTab, WalletTabType};
|
||||
use crate::gui::views::wallets::wallet::account::AccountContent;
|
||||
use crate::gui::views::wallets::wallet::request::{InvoiceRequestContent, SendRequestContent};
|
||||
use crate::gui::views::wallets::wallet::transport::WalletTransportContent;
|
||||
use crate::gui::views::wallets::wallet::types::WalletContentContainer;
|
||||
use crate::gui::views::wallets::wallet::WalletSettings;
|
||||
use crate::gui::views::wallets::WalletTransactions;
|
||||
use crate::gui::views::wallets::wallet::{WalletSettingsContent, WalletTransactionsContent};
|
||||
use crate::gui::views::{Content, FilePickContent, FilePickContentType, Modal, View};
|
||||
use crate::gui::Colors;
|
||||
use crate::node::Node;
|
||||
@@ -35,8 +33,11 @@ use crate::AppConfig;
|
||||
|
||||
/// Wallet content.
|
||||
pub struct WalletContent {
|
||||
/// Current tab content to show.
|
||||
current_tab: Box<dyn WalletTab>,
|
||||
/// Transactions content.
|
||||
pub txs_content: Option<WalletTransactionsContent>,
|
||||
|
||||
/// Settings content.
|
||||
pub settings_content: Option<WalletSettingsContent>,
|
||||
|
||||
/// Account panel content.
|
||||
pub account_content: AccountContent,
|
||||
@@ -44,9 +45,9 @@ pub struct WalletContent {
|
||||
pub transport_content: WalletTransportContent,
|
||||
|
||||
/// Invoice request creation [`Modal`] content.
|
||||
invoice_request_content: Option<InvoiceRequestContent>,
|
||||
invoice_content: Option<InvoiceRequestContent>,
|
||||
/// Send request creation [`Modal`] content.
|
||||
send_request_content: Option<SendRequestContent>,
|
||||
send_content: Option<SendRequestContent>,
|
||||
|
||||
/// Tab button to pick file for parsing.
|
||||
file_pick_tab_button: FilePickContent,
|
||||
@@ -68,12 +69,12 @@ impl WalletContentContainer for WalletContent {
|
||||
fn modal_ui(&mut self, ui: &mut egui::Ui, w: &Wallet, m: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
match m.id {
|
||||
INVOICE_MODAL_ID => {
|
||||
if let Some(c) = self.invoice_request_content.as_mut() {
|
||||
if let Some(c) = self.invoice_content.as_mut() {
|
||||
c.modal_ui(ui, w, m, cb);
|
||||
}
|
||||
}
|
||||
SEND_MODAL_ID => {
|
||||
if let Some(c) = self.send_request_content.as_mut() {
|
||||
if let Some(c) = self.send_content.as_mut() {
|
||||
c.modal_ui(ui, w, m, cb);
|
||||
}
|
||||
}
|
||||
@@ -91,7 +92,7 @@ impl WalletContentContainer for WalletContent {
|
||||
|
||||
// Show wallet account panel not on settings tab when navigation is not blocked and QR code
|
||||
// scanner is not showing and wallet data is not empty.
|
||||
let mut show_account = self.current_tab.get_type() != WalletTabType::Settings && !block_nav
|
||||
let mut show_account = self.settings_content.is_none() && !block_nav
|
||||
&& !wallet.sync_error() && data.is_some();
|
||||
if wallet.get_current_connection() == ConnectionMethod::Integrated &&
|
||||
!Node::is_running() {
|
||||
@@ -222,7 +223,7 @@ impl WalletContentContainer for WalletContent {
|
||||
top: 0.0 as i8,
|
||||
bottom: 4.0 as i8,
|
||||
},
|
||||
fill: if self.current_tab.get_type() == WalletTabType::Settings {
|
||||
fill: if self.settings_content.is_some() {
|
||||
Colors::fill_lite()
|
||||
} else {
|
||||
Colors::TRANSPARENT
|
||||
@@ -231,25 +232,69 @@ impl WalletContentContainer for WalletContent {
|
||||
})
|
||||
.show_inside(ui, |ui| {
|
||||
let rect = ui.available_rect_before_wrap();
|
||||
let tab_type = self.current_tab.get_type();
|
||||
let show_sync = (tab_type != WalletTabType::Settings || block_nav) &&
|
||||
let show_settings = self.settings_content.is_some();
|
||||
let show_txs = self.txs_content.is_some();
|
||||
let show_sync = (!show_settings || block_nav) &&
|
||||
sync_ui(ui, &wallet);
|
||||
if !show_sync {
|
||||
if tab_type != WalletTabType::Txs {
|
||||
if show_settings {
|
||||
ui.add_space(3.0);
|
||||
ScrollArea::vertical()
|
||||
.id_salt(Id::from("wallet_tab_content_scroll")
|
||||
.with(tab_type.name())
|
||||
.with(wallet_id))
|
||||
.id_salt(Id::from("wallet_tab_content_scroll").with(wallet_id))
|
||||
.auto_shrink([false; 2])
|
||||
.scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden)
|
||||
.show(ui, |ui| {
|
||||
View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| {
|
||||
self.current_tab.ui(ui, &wallet, cb);
|
||||
self.settings_content
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.ui(ui, &wallet, cb);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
self.current_tab.ui(ui, &wallet, cb);
|
||||
} else if show_txs {
|
||||
self.txs_content
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.ui(ui, &wallet, cb);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let rect = {
|
||||
@@ -276,11 +321,12 @@ impl WalletContentContainer for WalletContent {
|
||||
impl Default for WalletContent {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
current_tab: Box::new(WalletTransactions::new(None)),
|
||||
txs_content: Some(WalletTransactionsContent::new(None)),
|
||||
settings_content: None,
|
||||
account_content: AccountContent::default(),
|
||||
transport_content: WalletTransportContent::default(),
|
||||
invoice_request_content: None,
|
||||
send_request_content: None,
|
||||
invoice_content: None,
|
||||
send_content: None,
|
||||
file_pick_tab_button: FilePickContent::new(FilePickContentType::Tab),
|
||||
}
|
||||
}
|
||||
@@ -297,8 +343,10 @@ impl WalletContent {
|
||||
t!("wallets.transport")
|
||||
} else if self.transport_content.qr_address_content.is_some() {
|
||||
t!("network_mining.address")
|
||||
} else if self.settings_content.is_some() {
|
||||
t!("wallets.settings")
|
||||
} else {
|
||||
self.current_tab.get_type().name()
|
||||
t!("wallets.txs")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,12 +390,13 @@ impl WalletContent {
|
||||
let can_send = has_wallet_data &&
|
||||
wallet.get_data().unwrap().info.amount_currently_spendable > 0;
|
||||
|
||||
let current_type = self.current_tab.get_type();
|
||||
let tabs_amount = if can_send { 5 } else { 4 };
|
||||
ui.columns(tabs_amount, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::tab_button(ui, STACK, None, Some(current_type == WalletTabType::Txs), |_| {
|
||||
self.current_tab = Box::new(WalletTransactions::new(None));
|
||||
let active = self.settings_content.is_none() && self.txs_content.is_some();
|
||||
View::tab_button(ui, STACK, None, Some(active), |_| {
|
||||
self.txs_content = Some(WalletTransactionsContent::new(None));
|
||||
self.settings_content = None;
|
||||
});
|
||||
});
|
||||
let active = if has_wallet_data { Some(false) } else { None };
|
||||
@@ -358,7 +407,7 @@ impl WalletContent {
|
||||
} else {
|
||||
let (icon, color) = (FILE_ARROW_DOWN, Some(Colors::green()));
|
||||
View::tab_button(ui, icon, color, active, |_| {
|
||||
self.invoice_request_content = Some(InvoiceRequestContent::default());
|
||||
self.invoice_content = Some(InvoiceRequestContent::default());
|
||||
Modal::new(INVOICE_MODAL_ID)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("wallets.receive"))
|
||||
@@ -385,7 +434,7 @@ impl WalletContent {
|
||||
} else {
|
||||
let (icon, color) = (FILE_ARROW_UP, Some(Colors::red()));
|
||||
View::tab_button(ui, icon, color, Some(false), |_| {
|
||||
self.send_request_content = Some(SendRequestContent::new(None));
|
||||
self.send_content = Some(SendRequestContent::new(None));
|
||||
Modal::new(SEND_MODAL_ID)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("wallets.send"))
|
||||
@@ -395,10 +444,11 @@ impl WalletContent {
|
||||
});
|
||||
}
|
||||
columns[tabs_amount - 1].vertical_centered_justified(|ui| {
|
||||
let active = Some(current_type == WalletTabType::Settings);
|
||||
View::tab_button(ui, GEAR_FINE, None, active, |ui| {
|
||||
let active = self.settings_content.is_some();
|
||||
View::tab_button(ui, GEAR_FINE, None, Some(active), |ui| {
|
||||
ExternalConnection::check(None, ui.ctx());
|
||||
self.current_tab = Box::new(WalletSettings::default());
|
||||
self.txs_content = None;
|
||||
self.settings_content = Some(WalletSettingsContent::default());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
use egui::{Id, RichText};
|
||||
use grin_core::core::amount_from_hr_string;
|
||||
use grin_core::core::{amount_from_hr_string, amount_to_hr_string};
|
||||
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, TextEdit, View};
|
||||
@@ -79,21 +79,21 @@ impl InvoiceRequestContent {
|
||||
if !self.amount_edit.is_empty() {
|
||||
self.amount_edit = self.amount_edit.trim().replace(",", ".");
|
||||
match amount_from_hr_string(self.amount_edit.as_str()) {
|
||||
Ok(a) => {
|
||||
Ok(amount) => {
|
||||
if !self.amount_edit.contains(".") {
|
||||
// To avoid input of several "0".
|
||||
if a == 0 {
|
||||
self.amount_edit = "0".to_string();
|
||||
return;
|
||||
// To avoid input of several `0` before `.` and put `.` after first `0`.
|
||||
if self.amount_edit.len() != 1 && self.amount_edit.starts_with("0") {
|
||||
let amount_text = amount_to_hr_string(amount, true);
|
||||
let amount_parts = amount_text.split(".").collect::<Vec<&str>>();
|
||||
self.amount_edit = format!("0.{}", amount_parts[0]);
|
||||
amount_edit.cursor_to_end(self.amount_edit.len(), ui);
|
||||
}
|
||||
} else {
|
||||
// Check input after ".".
|
||||
let parts = self.amount_edit
|
||||
.split(".")
|
||||
.collect::<Vec<&str>>();
|
||||
if parts.len() == 2 && parts[1].len() > 9 {
|
||||
// Check input after `.`.
|
||||
let parts = self.amount_edit.split(".").collect::<Vec<&str>>();
|
||||
if parts.len() == 2 && (parts[1].len() > 9 ||
|
||||
(amount == 0 && parts[1].len() > 8)) {
|
||||
self.amount_edit = amount_edit_before;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
use egui::{Id, RichText};
|
||||
use grin_core::core::{amount_from_hr_string, amount_to_hr_string};
|
||||
use grin_core::global::get_accept_fee_base;
|
||||
use grin_wallet_libwallet::SlatepackAddress;
|
||||
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{CameraContent, Modal, TextEdit, View};
|
||||
use crate::gui::Colors;
|
||||
@@ -24,8 +24,14 @@ use crate::wallet::Wallet;
|
||||
|
||||
/// Content to create a request to send funds.
|
||||
pub struct SendRequestContent {
|
||||
/// Amount to send or receive.
|
||||
/// Amount to send.
|
||||
amount_edit: String,
|
||||
/// Flag to check if maximum amount is calculating.
|
||||
pub max_calculating: bool,
|
||||
|
||||
/// Fee amount.
|
||||
fee_edit: String,
|
||||
|
||||
/// Receiver address.
|
||||
address_edit: String,
|
||||
/// Flag to check if entered address is incorrect.
|
||||
@@ -40,12 +46,26 @@ impl SendRequestContent {
|
||||
pub fn new(addr: Option<String>) -> Self {
|
||||
Self {
|
||||
amount_edit: "".to_string(),
|
||||
max_calculating: false,
|
||||
fee_edit: "".to_string(),
|
||||
address_edit: addr.unwrap_or("".to_string()),
|
||||
address_error: false,
|
||||
address_scan_content: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Setup fee amount.
|
||||
pub fn on_fee_calculated(&mut self, fee: u64) {
|
||||
self.fee_edit = amount_to_hr_string(fee, true);
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// Draw [`Modal`] content.
|
||||
pub fn modal_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
@@ -109,45 +129,107 @@ impl SendRequestContent {
|
||||
.h_center()
|
||||
.numeric()
|
||||
.focus(Modal::first_draw());
|
||||
if self.max_calculating {
|
||||
amount_edit = amount_edit.disable();
|
||||
}
|
||||
let amount_edit_before = self.amount_edit.clone();
|
||||
amount_edit.ui(ui, &mut self.amount_edit, cb);
|
||||
|
||||
// Draw button to calculate maximum amount to send.
|
||||
let mut calculate_max = false;
|
||||
amount_edit.custom_buttons_ui(ui, &mut self.amount_edit, cb, |ui| {
|
||||
if self.max_calculating {
|
||||
ui.add_space(12.0);
|
||||
View::loading_spinner(ui, 40.0);
|
||||
ui.add_space(12.0);
|
||||
} else {
|
||||
View::button(ui, t!("max_short"), Colors::white_or_black(false), || {
|
||||
calculate_max = true;
|
||||
});
|
||||
ui.add_space(8.0);
|
||||
}
|
||||
});
|
||||
if calculate_max {
|
||||
self.max_calculating = true;
|
||||
let max = wallet.get_data().unwrap().info.amount_currently_spendable;
|
||||
self.amount_edit = amount_to_hr_string(max, true);
|
||||
}
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Check value if input was changed.
|
||||
if amount_edit_before != self.amount_edit {
|
||||
if !self.amount_edit.is_empty() {
|
||||
// Trim text, replace "," by "." and parse amount.
|
||||
// Trim text, replace `,` by `.` and parse amount.
|
||||
self.amount_edit = self.amount_edit.trim().replace(",", ".");
|
||||
match amount_from_hr_string(self.amount_edit.as_str()) {
|
||||
Ok(a) => {
|
||||
Ok(mut amount) => {
|
||||
if !self.amount_edit.contains(".") {
|
||||
// To avoid input of several "0".
|
||||
if a == 0 {
|
||||
self.amount_edit = "0".to_string();
|
||||
return;
|
||||
// To avoid input of several `0` before `.` and put `.` after first `0`.
|
||||
if self.amount_edit.len() != 1 && self.amount_edit.starts_with("0") {
|
||||
let amount_text = amount_to_hr_string(amount, true);
|
||||
let amount_parts = amount_text.split(".").collect::<Vec<&str>>();
|
||||
self.amount_edit = format!("0.{}", amount_parts[0]);
|
||||
amount = amount_from_hr_string(self.amount_edit.as_str())
|
||||
.unwrap_or_else(|_| amount);
|
||||
amount_edit.cursor_to_end(self.amount_edit.len(), ui);
|
||||
}
|
||||
// Reset fee amount on `0`.
|
||||
if amount == 0 {
|
||||
self.fee_edit = "".to_string();
|
||||
}
|
||||
} else {
|
||||
// Check input after `.`.
|
||||
let parts = self.amount_edit.split(".").collect::<Vec<&str>>();
|
||||
if parts.len() == 2 && parts[1].len() > 9 {
|
||||
self.amount_edit = amount_edit_before;
|
||||
return;
|
||||
if parts.len() == 2 && (parts[1].len() > 9 ||
|
||||
(amount == 0 && parts[1].len() > 8)) {
|
||||
self.amount_edit = amount_edit_before.clone();
|
||||
}
|
||||
}
|
||||
|
||||
// Do not input amount more than balance in sending.
|
||||
let b = wallet.get_data().unwrap().info.amount_currently_spendable;
|
||||
if b < a {
|
||||
self.amount_edit = amount_edit_before;
|
||||
// Do not input amount more than balance.
|
||||
if amount != 0 && self.amount_edit != amount_edit_before {
|
||||
let fee = amount_from_hr_string(self.fee_edit.as_str()).unwrap_or(0);
|
||||
let max = wallet.get_data().unwrap().info.amount_currently_spendable;
|
||||
if amount > max || amount + fee > max {
|
||||
self.max_calculating = true;
|
||||
wallet.task(WalletTask::CalculateFee(max, 0));
|
||||
} else {
|
||||
wallet.task(WalletTask::CalculateFee(amount, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
self.amount_edit = amount_edit_before;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.fee_edit = "".to_string();
|
||||
}
|
||||
}
|
||||
|
||||
// Show fee value.
|
||||
ui.vertical_centered(|ui| {
|
||||
let fee_label = t!(
|
||||
"wallets.fee_base_desc",
|
||||
"value" => format!(": {}", get_accept_fee_base())
|
||||
);
|
||||
ui.label(RichText::new(fee_label)
|
||||
.size(17.0)
|
||||
.color(Colors::gray()));
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
let fee_edit_id = Id::from(modal.id).with("_fee").with(wallet.get_config().id);
|
||||
let mut fee_edit = TextEdit::new(fee_edit_id)
|
||||
.focus(false)
|
||||
.h_center()
|
||||
.disable();
|
||||
let mut loading_label = format!("{}...", t!("wallets.loading"));
|
||||
fee_edit.ui(ui, if wallet.fee_calculating() {
|
||||
&mut loading_label
|
||||
} else {
|
||||
&mut self.fee_edit
|
||||
}, cb);
|
||||
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Show address error or input description.
|
||||
ui.vertical_centered(|ui| {
|
||||
if self.address_error {
|
||||
@@ -179,12 +261,10 @@ impl SendRequestContent {
|
||||
self.address_scan_content = Some(CameraContent::default());
|
||||
}
|
||||
ui.add_space(12.0);
|
||||
|
||||
// Check value if input was changed.
|
||||
if addr_edit_before != self.address_edit {
|
||||
self.address_error = false;
|
||||
}
|
||||
|
||||
// Continue on Enter press.
|
||||
if address_edit.enter_pressed {
|
||||
self.on_continue(wallet);
|
||||
@@ -211,7 +291,7 @@ impl SendRequestContent {
|
||||
|
||||
/// Callback when Continue button was pressed.
|
||||
fn on_continue(&mut self, wallet: &Wallet) {
|
||||
if self.amount_edit.is_empty() {
|
||||
if self.amount_edit.is_empty() || self.max_calculating || wallet.fee_calculating() {
|
||||
return;
|
||||
}
|
||||
// Check address to send over Tor if enabled.
|
||||
|
||||
@@ -15,12 +15,11 @@
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::types::ContentContainer;
|
||||
use crate::gui::views::wallets::{CommonSettings, ConnectionSettings, RecoverySettings};
|
||||
use crate::gui::views::wallets::types::{WalletTab, WalletTabType};
|
||||
use crate::gui::views::wallets::wallet::types::WalletContentContainer;
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
/// Wallet settings tab content.
|
||||
pub struct WalletSettings {
|
||||
pub struct WalletSettingsContent {
|
||||
/// Common setup content.
|
||||
common_setup: CommonSettings,
|
||||
/// Connection setup content.
|
||||
@@ -29,7 +28,7 @@ pub struct WalletSettings {
|
||||
recovery_setup: RecoverySettings
|
||||
}
|
||||
|
||||
impl Default for WalletSettings {
|
||||
impl Default for WalletSettingsContent {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
common_setup: CommonSettings::default(),
|
||||
@@ -39,12 +38,8 @@ impl Default for WalletSettings {
|
||||
}
|
||||
}
|
||||
|
||||
impl WalletTab for WalletSettings {
|
||||
fn get_type(&self) -> WalletTabType {
|
||||
WalletTabType::Settings
|
||||
}
|
||||
|
||||
fn ui(&mut self,
|
||||
impl WalletSettingsContent {
|
||||
pub fn ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
wallet: &Wallet,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
|
||||
@@ -24,8 +24,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use crate::gui::icons::{ARCHIVE_BOX, ARROWS_CLOCKWISE, ARROW_CIRCLE_DOWN, ARROW_CIRCLE_UP, CALENDAR_CHECK, DOTS_THREE_CIRCLE, FILE_ARROW_DOWN, FILE_TEXT, GEAR_FINE, PROHIBIT, WARNING, X_CIRCLE};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::types::{LinePosition, ModalPosition};
|
||||
use crate::gui::views::wallets::types::WalletTab;
|
||||
use crate::gui::views::wallets::wallet::types::{WalletTabType, GRIN};
|
||||
use crate::gui::views::wallets::wallet::types::{WalletContentContainer, GRIN};
|
||||
use crate::gui::views::wallets::wallet::WalletTransactionContent;
|
||||
use crate::gui::views::{Content, Modal, PullToRefresh, View};
|
||||
use crate::gui::Colors;
|
||||
@@ -33,7 +32,7 @@ use crate::wallet::types::{WalletData, WalletTask, WalletTransaction, WalletTran
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
/// Wallet transactions tab content.
|
||||
pub struct WalletTransactions {
|
||||
pub struct WalletTransactionsContent {
|
||||
/// Transaction information [`Modal`] content.
|
||||
tx_info_content: Option<WalletTransactionContent>,
|
||||
|
||||
@@ -44,22 +43,26 @@ pub struct WalletTransactions {
|
||||
manual_sync: Option<u128>
|
||||
}
|
||||
|
||||
impl WalletTab for WalletTransactions {
|
||||
fn get_type(&self) -> WalletTabType {
|
||||
WalletTabType::Txs
|
||||
impl WalletContentContainer for WalletTransactionsContent {
|
||||
fn modal_ids(&self) -> Vec<&'static str> {
|
||||
vec![TX_INFO_MODAL, CANCEL_TX_CONFIRMATION_MODAL]
|
||||
}
|
||||
|
||||
fn ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) {
|
||||
if Modal::opened().is_none() {
|
||||
// Show transaction modal on task result.
|
||||
if let Some(id) = wallet.consume_tx_task_result() {
|
||||
let tx = wallet.get_data().unwrap().tx_by_slate_id(id);
|
||||
if let Some(tx) = tx {
|
||||
self.show_tx_info_modal(tx.data.id);
|
||||
fn modal_ui(&mut self, ui: &mut egui::Ui, w: &Wallet, m: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
match m.id {
|
||||
TX_INFO_MODAL => {
|
||||
if let Some(content) = self.tx_info_content.as_mut() {
|
||||
content.ui(ui, w, m, cb);
|
||||
}
|
||||
}
|
||||
CANCEL_TX_CONFIRMATION_MODAL => {
|
||||
self.cancel_confirmation_modal(ui, w);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
self.modal_content_ui(ui, wallet, cb);
|
||||
}
|
||||
|
||||
fn container_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, _: &dyn PlatformCallbacks) {
|
||||
self.txs_ui(ui, wallet);
|
||||
}
|
||||
}
|
||||
@@ -69,7 +72,7 @@ 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";
|
||||
|
||||
impl WalletTransactions {
|
||||
impl WalletTransactionsContent {
|
||||
/// Height of transaction list item.
|
||||
pub const TX_ITEM_HEIGHT: f32 = 75.0;
|
||||
|
||||
@@ -122,6 +125,7 @@ impl WalletTransactions {
|
||||
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis();
|
||||
let refresh = self.manual_sync.unwrap_or(0) + 1600 > now;
|
||||
let refresh_resp = PullToRefresh::new(refresh)
|
||||
.id(Id::from("refresh_tx_list").with(config.id))
|
||||
.can_refresh(!refresh && !wallet.syncing())
|
||||
.min_refresh_distance(70.0)
|
||||
.scroll_area_ui(ui, |ui| {
|
||||
@@ -241,33 +245,6 @@ impl WalletTransactions {
|
||||
ui.painter().set(bg_idx, bg);
|
||||
}
|
||||
|
||||
/// Draw [`Modal`] content for this ui container.
|
||||
fn modal_content_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
wallet: &Wallet,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
match Modal::opened() {
|
||||
None => {}
|
||||
Some(id) => {
|
||||
match id {
|
||||
TX_INFO_MODAL => {
|
||||
Modal::ui(ui.ctx(), cb, |ui, modal, cb| {
|
||||
if let Some(content) = self.tx_info_content.as_mut() {
|
||||
content.ui(ui, wallet, modal, cb);
|
||||
}
|
||||
});
|
||||
}
|
||||
CANCEL_TX_CONFIRMATION_MODAL => {
|
||||
Modal::ui(ui.ctx(), cb, |ui, _, _| {
|
||||
self.cancel_confirmation_modal(ui, wallet);
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw transaction item.
|
||||
pub fn tx_item_ui(ui: &mut egui::Ui,
|
||||
tx: &WalletTransaction,
|
||||
@@ -417,7 +394,7 @@ impl WalletTransactions {
|
||||
TxLogEntryType::TxSent | TxLogEntryType::TxReceived => {
|
||||
let min_conf = data.info.minimum_confirmations;
|
||||
if tx.height.is_none() || (tx.height.unwrap() != 0 &&
|
||||
height - tx.height.unwrap() > min_conf - 1) {
|
||||
height - tx.height.unwrap() >= min_conf - 1) {
|
||||
let (i, t) = if tx.data.tx_type == TxLogEntryType::TxSent {
|
||||
(ARROW_CIRCLE_UP, t!("wallets.tx_sent"))
|
||||
} else {
|
||||
|
||||
@@ -19,7 +19,7 @@ use grin_wallet_libwallet::TxLogEntryType;
|
||||
|
||||
use crate::gui::icons::{CIRCLE_HALF, COPY, CUBE, FILE_ARCHIVE, FILE_TEXT, HASH_STRAIGHT, PROHIBIT, QR_CODE, SCAN};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::wallets::wallet::txs::WalletTransactions;
|
||||
use crate::gui::views::wallets::wallet::txs::WalletTransactionsContent;
|
||||
use crate::gui::views::{CameraContent, FilePickContent, FilePickContentType, Modal, QrCodeContent, View};
|
||||
use crate::gui::Colors;
|
||||
use crate::wallet::types::{WalletTask, WalletTransaction};
|
||||
@@ -220,7 +220,7 @@ impl WalletTransactionContent {
|
||||
ui.add_space(6.0);
|
||||
|
||||
let mut rect = ui.available_rect_before_wrap();
|
||||
rect.set_height(WalletTransactions::TX_ITEM_HEIGHT);
|
||||
rect.set_height(WalletTransactionsContent::TX_ITEM_HEIGHT);
|
||||
|
||||
// Draw tx item background.
|
||||
let p = ui.painter();
|
||||
@@ -229,7 +229,7 @@ impl WalletTransactionContent {
|
||||
|
||||
// Show transaction amount status and time.
|
||||
let data = wallet.get_data().unwrap();
|
||||
WalletTransactions::tx_item_ui(ui, tx, rect, &data, |ui| {
|
||||
WalletTransactionsContent::tx_item_ui(ui, tx, rect, &data, |ui| {
|
||||
// Show block height or buttons.
|
||||
if let Some(h) = tx.height {
|
||||
if h != 0 {
|
||||
@@ -281,7 +281,7 @@ impl WalletTransactionContent {
|
||||
} else {
|
||||
View::item_rounding(0, 2, true)
|
||||
};
|
||||
WalletTransactions::tx_repeat_button_ui(ui, r, tx, wallet, rebroadcast);
|
||||
WalletTransactionsContent::tx_repeat_button_ui(ui, r, tx, wallet, rebroadcast);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -42,32 +42,6 @@ pub trait WalletContentContainer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wallet tab content interface.
|
||||
pub trait WalletTab {
|
||||
fn get_type(&self) -> WalletTabType;
|
||||
fn ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
wallet: &Wallet,
|
||||
cb: &dyn PlatformCallbacks);
|
||||
}
|
||||
|
||||
/// Type of [`WalletTab`] content.
|
||||
#[derive(PartialEq)]
|
||||
pub enum WalletTabType {
|
||||
Txs,
|
||||
Settings
|
||||
}
|
||||
|
||||
impl WalletTabType {
|
||||
/// Name of wallet tab to show at ui.
|
||||
pub fn name(&self) -> String {
|
||||
match *self {
|
||||
WalletTabType::Txs => t!("wallets.txs"),
|
||||
WalletTabType::Settings => t!("wallets.settings")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get wallet status text.
|
||||
pub fn wallet_status_text(wallet: &Wallet) -> String {
|
||||
if wallet.is_open() {
|
||||
|
||||
@@ -394,6 +394,10 @@ impl WalletTransaction {
|
||||
pub enum WalletTask {
|
||||
/// Open Slatepack message parsing result and making an action.
|
||||
OpenMessage(String),
|
||||
/// Calculate fee to send amount.
|
||||
/// * amount
|
||||
/// * fee (to read at result)
|
||||
CalculateFee(u64, u64),
|
||||
/// Create request to send.
|
||||
/// * amount
|
||||
/// * receiver
|
||||
|
||||
+168
-101
@@ -32,7 +32,7 @@ use grin_wallet_controller::command::parse_slatepack;
|
||||
use grin_wallet_controller::controller;
|
||||
use grin_wallet_controller::controller::ForeignAPIHandlerV2;
|
||||
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient, LMDBBackend};
|
||||
use grin_wallet_libwallet::api_impl::owner::{cancel_tx, retrieve_summary_info, retrieve_txs};
|
||||
use grin_wallet_libwallet::api_impl::owner::{cancel_tx, init_send_tx, retrieve_summary_info, retrieve_txs};
|
||||
use grin_wallet_libwallet::{address, Error, InitTxArgs, IssueInvoiceTxArgs, NodeClient, RetrieveTxQueryArgs, RetrieveTxQuerySortField, RetrieveTxQuerySortOrder, Slate, SlateState, SlateVersion, SlatepackAddress, StatusMessage, TxLogEntry, TxLogEntryType, VersionedSlate, WalletBackend, WalletInfo, WalletInitStatus, WalletInst, WalletLCProvider};
|
||||
use grin_wallet_util::OnionV3Address;
|
||||
use parking_lot::RwLock;
|
||||
@@ -100,6 +100,8 @@ pub struct Wallet {
|
||||
/// Flag to check if Slatepack message file is opening.
|
||||
message_opening: Arc<AtomicBool>,
|
||||
|
||||
/// Amount requests to calculate fee.
|
||||
fee_calculating: Arc<AtomicU8>,
|
||||
/// Flag to check if sending request is creating.
|
||||
send_creating: Arc<AtomicBool>,
|
||||
/// Flag to check if invoice is creating.
|
||||
@@ -107,9 +109,8 @@ pub struct Wallet {
|
||||
|
||||
/// Tasks sender.
|
||||
tasks_sender: Arc<RwLock<Option<Sender<WalletTask>>>>,
|
||||
/// Transaction identifier after successful task completion.
|
||||
/// To be replaced with https://github.com/lucasmerlin/hello_egui/tree/main/crates/egui_inbox.
|
||||
task_result_slate_id: Arc<RwLock<Option<String>>>,
|
||||
/// Task result with optional transaction identifier.
|
||||
task_result: Arc<RwLock<Option<(Option<String>, WalletTask)>>>,
|
||||
}
|
||||
|
||||
impl Wallet {
|
||||
@@ -137,9 +138,10 @@ impl Wallet {
|
||||
repair_progress: Arc::new(AtomicU8::new(0)),
|
||||
message_opening: Arc::new(AtomicBool::from(false)),
|
||||
send_creating: Arc::new(AtomicBool::new(false)),
|
||||
fee_calculating: Arc::new(AtomicU8::new(0)),
|
||||
invoice_creating: Arc::new(AtomicBool::new(false)),
|
||||
tasks_sender: Arc::new(RwLock::new(None)),
|
||||
task_result_slate_id: Arc::new(RwLock::new(None)),
|
||||
task_result: Arc::new(RwLock::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,6 +588,13 @@ impl Wallet {
|
||||
pub fn task(&self, task: WalletTask) {
|
||||
let r_tasks = self.tasks_sender.read();
|
||||
if r_tasks.is_some() {
|
||||
match task {
|
||||
WalletTask::CalculateFee(_, _) => {
|
||||
let calculating = self.fee_calculating.load(Ordering::Relaxed);
|
||||
self.fee_calculating.store(calculating + 1, Ordering::Relaxed);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let _ = r_tasks.as_ref().unwrap().send(task);
|
||||
}
|
||||
}
|
||||
@@ -706,76 +715,6 @@ impl Wallet {
|
||||
None
|
||||
}
|
||||
|
||||
/// Open Slatepack message with the wallet.
|
||||
fn open_message(&self, message: &String) {
|
||||
if !self.is_open() || message.is_empty() {
|
||||
return;
|
||||
}
|
||||
let w = self.clone();
|
||||
let load = self.message_opening.clone();
|
||||
let msg = message.clone();
|
||||
thread::spawn(move || {
|
||||
load.store(true, Ordering::Relaxed);
|
||||
if let Ok(s) = w.parse_slatepack(&msg) {
|
||||
// Check if message with same id already exists.
|
||||
let exists = {
|
||||
let mut exists = w.slatepack_exists(&s);
|
||||
if !exists && (s.state == SlateState::Invoice2 ||
|
||||
s.state == SlateState::Standard2) {
|
||||
let mut slate = s.clone();
|
||||
slate.state = if s.state == SlateState::Standard2 {
|
||||
SlateState::Standard3
|
||||
} else {
|
||||
SlateState::Invoice3
|
||||
};
|
||||
exists = w.slatepack_exists(&slate);
|
||||
}
|
||||
exists
|
||||
};
|
||||
if exists {
|
||||
w.on_tx_result(&s);
|
||||
load.store(false, Ordering::Relaxed);
|
||||
return;
|
||||
}
|
||||
// Create response or finalize.
|
||||
match s.state {
|
||||
SlateState::Standard1 | SlateState::Invoice1 => {
|
||||
if s.state != SlateState::Standard1 {
|
||||
if let Ok(s) = w.pay(&s) {
|
||||
sync_wallet_data(&w, false);
|
||||
w.on_tx_result(&s);
|
||||
}
|
||||
} else {
|
||||
if let Ok(s) = w.receive(&s) {
|
||||
sync_wallet_data(&w, false);
|
||||
w.on_tx_result(&s);
|
||||
}
|
||||
}
|
||||
}
|
||||
SlateState::Standard2 | SlateState::Invoice2 => {
|
||||
match w.finalize(&s) {
|
||||
Ok(s) => {
|
||||
match w.post(&s) {
|
||||
Ok(_) => {
|
||||
sync_wallet_data(&w, false);
|
||||
}
|
||||
Err(e) => {
|
||||
w.on_tx_error(s.id.to_string(), Some(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
w.on_tx_error(s.id.to_string(), Some(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
load.store(false, Ordering::Relaxed);
|
||||
});
|
||||
}
|
||||
|
||||
/// Check if Slatepack message is opening.
|
||||
pub fn message_opening(&self) -> bool {
|
||||
self.message_opening.load(Ordering::Relaxed)
|
||||
@@ -826,7 +765,46 @@ impl Wallet {
|
||||
None
|
||||
}
|
||||
|
||||
/// Initialize a transaction to send amount, return request for funds receiver.
|
||||
/// Calculate transaction fee for provided amount.
|
||||
fn calculate_fee(&self, a: u64) -> Result<u64, Error> {
|
||||
let r_inst = self.instance.as_ref().read();
|
||||
let instance = r_inst.clone().unwrap();
|
||||
let mut w_lock = instance.lock();
|
||||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
let config = self.get_config();
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: Some(config.account.clone()),
|
||||
amount: a,
|
||||
minimum_confirmations: config.min_confirmations,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: false,
|
||||
estimate_only: Some(true),
|
||||
..Default::default()
|
||||
};
|
||||
let res = init_send_tx(&mut **w, None, args, false);
|
||||
match res {
|
||||
Ok(slate) => {
|
||||
Ok(slate.fee_fields.fee())
|
||||
}
|
||||
Err(e) => {
|
||||
match e {
|
||||
Error::NotEnoughFunds { available, needed, .. } => {
|
||||
Ok(needed - available)
|
||||
},
|
||||
e => {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if transaction fee is calculating.
|
||||
pub fn fee_calculating(&self) -> bool {
|
||||
self.fee_calculating.load(Ordering::Relaxed) > 0
|
||||
}
|
||||
|
||||
/// Initialize a transaction to send amount.
|
||||
fn send(&self, a: u64, r: Option<SlatepackAddress>) -> Result<Slate, Error> {
|
||||
let config = self.get_config();
|
||||
let args = InitTxArgs {
|
||||
@@ -1017,27 +995,25 @@ impl Wallet {
|
||||
w_data.as_mut().unwrap().on_tx_error(id, err);
|
||||
}
|
||||
|
||||
/// Save identifier of successful transaction after tasks completion to consume later.
|
||||
fn on_tx_result(&self, slate: &Slate) {
|
||||
let mut w_res = self.task_result_slate_id.write();
|
||||
*w_res = Some(slate.id.to_string());
|
||||
/// Save task result to consume later.
|
||||
fn on_task_result(&self, id: Option<String>, task: &WalletTask) {
|
||||
let mut w_res = self.task_result.write();
|
||||
*w_res = Some((id, task.clone()));
|
||||
}
|
||||
|
||||
/// Consume identifier of successful transaction task.
|
||||
pub fn consume_tx_task_result(&self) -> Option<String> {
|
||||
/// Consume result of successful task.
|
||||
pub fn consume_task_result(&self) -> Option<(Option<String>, WalletTask)> {
|
||||
let res = {
|
||||
let r_res = self.task_result_slate_id.read();
|
||||
let r_res = self.task_result.read();
|
||||
r_res.clone()
|
||||
};
|
||||
// Clear result.
|
||||
if res.is_some() {
|
||||
let mut w_res = self.task_result_slate_id.write();
|
||||
*w_res = None;
|
||||
}
|
||||
// Clear result for transaction task.
|
||||
let mut w_res = self.task_result.write();
|
||||
*w_res = None;
|
||||
res
|
||||
}
|
||||
|
||||
/// Get possible transaction confirmation height, .
|
||||
/// Get possible transaction confirmation height.
|
||||
fn tx_height(&self, tx: &WalletTransaction) -> Result<Option<u64>, Error> {
|
||||
let mut tx_height = None;
|
||||
if tx.data.confirmed && tx.data.kernel_excess.is_some() {
|
||||
@@ -1176,7 +1152,7 @@ fn start_sync(wallet: Wallet) -> Thread {
|
||||
*w_tasks = Some(tx);
|
||||
}
|
||||
let wallet_thread = wallet.clone();
|
||||
thread::spawn(move || loop {
|
||||
thread::spawn(move || loop {
|
||||
let wallet_task = wallet_thread.clone();
|
||||
if let Ok(task) = rx.recv() {
|
||||
thread::spawn(move || {
|
||||
@@ -1186,7 +1162,8 @@ fn start_sync(wallet: Wallet) -> Thread {
|
||||
.unwrap()
|
||||
.block_on(async {
|
||||
handle_task(&wallet_task, task).await;
|
||||
})});
|
||||
});
|
||||
});
|
||||
}
|
||||
if wallet_thread.is_closing() || !wallet_thread.is_open() {
|
||||
break;
|
||||
@@ -1233,7 +1210,7 @@ fn start_sync(wallet: Wallet) -> Thread {
|
||||
// Reset loading progress.
|
||||
wallet.info_sync_progress.store(0, Ordering::Relaxed);
|
||||
}
|
||||
// Set an error when required integrated node is not enabled.
|
||||
// Set an error when integrated node is not enabled.
|
||||
wallet.set_sync_error(not_enabled);
|
||||
// Skip cycle when node sync is not finished.
|
||||
if !Node::is_running() || Node::get_sync_status() != Some(SyncStatus::NoSync) {
|
||||
@@ -1316,6 +1293,7 @@ fn start_sync(wallet: Wallet) -> Thread {
|
||||
/// Handle wallet task.
|
||||
async fn handle_task(w: &Wallet, t: WalletTask) {
|
||||
let send_tor = async |s: &Slate, r: &SlatepackAddress| {
|
||||
let id = s.id.to_string();
|
||||
match w.send_tor(&s, r).await {
|
||||
Ok(s) => {
|
||||
match w.finalize(&s) {
|
||||
@@ -1323,39 +1301,128 @@ async fn handle_task(w: &Wallet, t: WalletTask) {
|
||||
match w.post(&s) {
|
||||
Ok(_) => {
|
||||
sync_wallet_data(&w, false);
|
||||
w.on_tx_result(&s);
|
||||
w.on_task_result(Some(id), &t);
|
||||
}
|
||||
Err(e) => {
|
||||
w.on_tx_error(s.id.to_string(), Some(e));
|
||||
w.on_tx_error(id, Some(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
w.on_tx_error(s.id.to_string(), Some(e));
|
||||
w.on_tx_error(id, Some(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
w.on_tx_error(s.id.to_string(), Some(e));
|
||||
w.on_tx_result(&s);
|
||||
w.on_tx_error(id.clone(), Some(e));
|
||||
w.on_task_result(Some(id), &t);
|
||||
}
|
||||
}
|
||||
};
|
||||
match &t {
|
||||
WalletTask::OpenMessage(m) => {
|
||||
w.open_message(m);
|
||||
if !w.is_open() || m.is_empty() {
|
||||
return;
|
||||
}
|
||||
let w = w.clone();
|
||||
let load = w.message_opening.clone();
|
||||
let msg = m.clone();
|
||||
thread::spawn(move || {
|
||||
load.store(true, Ordering::Relaxed);
|
||||
if let Ok(s) = w.parse_slatepack(&msg) {
|
||||
let id = s.id.to_string();
|
||||
// Check if message already exists.
|
||||
let exists = {
|
||||
let mut exists = w.slatepack_exists(&s);
|
||||
if !exists && (s.state == SlateState::Invoice2 ||
|
||||
s.state == SlateState::Standard2) {
|
||||
let mut slate = s.clone();
|
||||
slate.state = if s.state == SlateState::Standard2 {
|
||||
SlateState::Standard3
|
||||
} else {
|
||||
SlateState::Invoice3
|
||||
};
|
||||
exists = w.slatepack_exists(&slate);
|
||||
}
|
||||
exists
|
||||
};
|
||||
if exists {
|
||||
w.on_task_result(Some(id), &t);
|
||||
load.store(false, Ordering::Relaxed);
|
||||
return;
|
||||
}
|
||||
// Create response or finalize.
|
||||
match s.state {
|
||||
SlateState::Standard1 | SlateState::Invoice1 => {
|
||||
if s.state != SlateState::Standard1 {
|
||||
if let Ok(_) = w.pay(&s) {
|
||||
sync_wallet_data(&w, false);
|
||||
w.on_task_result(Some(id), &t);
|
||||
}
|
||||
} else {
|
||||
if let Ok(_) = w.receive(&s) {
|
||||
sync_wallet_data(&w, false);
|
||||
w.on_task_result(Some(id), &t);
|
||||
}
|
||||
}
|
||||
}
|
||||
SlateState::Standard2 | SlateState::Invoice2 => {
|
||||
match w.finalize(&s) {
|
||||
Ok(s) => {
|
||||
match w.post(&s) {
|
||||
Ok(_) => {
|
||||
sync_wallet_data(&w, false);
|
||||
}
|
||||
Err(e) => {
|
||||
w.on_tx_error(id, Some(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
w.on_tx_error(id, Some(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
load.store(false, Ordering::Relaxed);
|
||||
});
|
||||
}
|
||||
WalletTask::CalculateFee(a, _) => {
|
||||
// Wait if there are no more fee tasks or handle next input value.
|
||||
let calculating = w.fee_calculating.load(Ordering::Relaxed);
|
||||
if calculating == 1 {
|
||||
async_std::task::sleep(Duration::from_millis(100)).await;
|
||||
let calculating = w.fee_calculating.load(Ordering::Relaxed);
|
||||
if calculating > 1 {
|
||||
w.fee_calculating.store(calculating - 1, Ordering::Relaxed);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
w.fee_calculating.store(calculating - 1, Ordering::Relaxed);
|
||||
return;
|
||||
}
|
||||
// Calculate fee for provided amount.
|
||||
if let Ok(fee) = w.calculate_fee(*a) {
|
||||
w.on_task_result(None, &WalletTask::CalculateFee(*a, fee))
|
||||
}
|
||||
let calculating = w.fee_calculating.load(Ordering::Relaxed);
|
||||
w.fee_calculating.store(calculating - 1, Ordering::Relaxed);
|
||||
}
|
||||
WalletTask::Send(a, r) => {
|
||||
w.send_creating.store(true, Ordering::Relaxed);
|
||||
if let Ok(s) = w.send(*a, r.clone()) {
|
||||
sync_wallet_data(&w, false);
|
||||
w.send_creating.store(false, Ordering::Relaxed);
|
||||
if let Some(r) = r {
|
||||
w.send_creating.store(false, Ordering::Relaxed);
|
||||
send_tor(&s, r).await;
|
||||
return;
|
||||
} else {
|
||||
w.on_tx_result(&s);
|
||||
w.on_task_result(Some(s.id.to_string()), &t);
|
||||
}
|
||||
}
|
||||
w.send_creating.store(false, Ordering::Relaxed);
|
||||
}
|
||||
WalletTask::SendTor(id, r) => {
|
||||
if let Some(s) = w.get_tx(*id) {
|
||||
@@ -1366,7 +1433,7 @@ async fn handle_task(w: &Wallet, t: WalletTask) {
|
||||
w.invoice_creating.store(true, Ordering::Relaxed);
|
||||
if let Ok(s) = w.issue_invoice(*a) {
|
||||
sync_wallet_data(&w, false);
|
||||
w.on_tx_result(&s);
|
||||
w.on_task_result(Some(s.id.to_string()), &t);
|
||||
}
|
||||
w.invoice_creating.store(false, Ordering::Relaxed);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user