Open .slatepack file with the app #13

Merged
ardocrat merged 28 commits from slatepack_ext_file into master 2024-09-16 19:08:27 +03:00
3 changed files with 256 additions and 201 deletions
Showing only changes of commit 1431e307ee - Show all commits

View file

@ -0,0 +1,240 @@
// 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 egui::{Align, Id, Layout, RichText, ScrollArea};
use egui::scroll_area::ScrollBarVisibility;
use grin_core::core::amount_to_hr_string;
use crate::gui::Colors;
use crate::gui::icons::{CHECK, CHECK_FAT, FOLDER_USER, PATH};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, View};
use crate::gui::views::types::TextEditOptions;
use crate::gui::views::wallets::wallet::types::GRIN;
use crate::wallet::types::WalletAccount;
use crate::wallet::{Wallet, WalletConfig};
/// Wallet accounts content.
pub struct WalletAccounts {
/// List of wallet accounts.
accounts: Vec<WalletAccount>,
/// Flag to check if account is creating.
account_creating: bool,
/// Account label value.
account_label_edit: String,
/// Flag to check if error occurred during account creation.
account_creation_error: bool,
}
impl Default for WalletAccounts {
fn default() -> Self {
Self {
accounts: vec![],
account_creating: false,
account_label_edit: "".to_string(),
account_creation_error: false,
}
}
}
impl WalletAccounts {
/// Create new instance from wallet accounts.
pub fn new(accounts: Vec<WalletAccount>) -> Self {
Self {
accounts,
account_creating: false,
account_label_edit: "".to_string(),
account_creation_error: false,
}
}
/// Draw [`Modal`] content.
pub fn ui(&mut self,
ui: &mut egui::Ui,
wallet: &mut Wallet,
modal: &Modal,
cb: &dyn PlatformCallbacks) {
if self.account_creating {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("wallets.new_account_desc"))
.size(17.0)
.color(Colors::gray()));
ui.add_space(8.0);
// Draw account name edit.
let text_edit_id = Id::from(modal.id).with(wallet.get_config().id);
let mut text_edit_opts = TextEditOptions::new(text_edit_id);
View::text_edit(ui, cb, &mut self.account_label_edit, &mut text_edit_opts);
// Show error occurred during account creation..
if self.account_creation_error {
ui.add_space(12.0);
ui.label(RichText::new(t!("error"))
.size(17.0)
.color(Colors::red()));
}
ui.add_space(12.0);
});
// Setup spacing between buttons.
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
// Show modal buttons.
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
columns[1].vertical_centered_justified(|ui| {
// Create button callback.
let mut on_create = || {
if !self.account_label_edit.is_empty() {
let label = &self.account_label_edit;
match wallet.create_account(label) {
Ok(_) => {
let _ = wallet.set_active_account(label);
cb.hide_keyboard();
modal.close();
},
Err(_) => self.account_creation_error = true
};
}
};
View::on_enter_key(ui, || {
(on_create)();
});
View::button(ui, t!("create"), Colors::white_or_black(false), on_create);
});
});
ui.add_space(6.0);
} else {
ui.add_space(3.0);
// Show list of accounts.
let size = self.accounts.len();
ScrollArea::vertical()
.id_source("account_list_modal_scroll")
.scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden)
.max_height(266.0)
.auto_shrink([true; 2])
.show_rows(ui, ACCOUNT_ITEM_HEIGHT, size, |ui, row_range| {
for index in row_range {
// Add space before the first item.
if index == 0 {
ui.add_space(4.0);
}
let acc = self.accounts.get(index).unwrap();
account_item_ui(ui, modal, wallet, acc, index, size);
if index == size - 1 {
ui.add_space(4.0);
}
}
});
ui.add_space(2.0);
View::horizontal_line(ui, Colors::stroke());
ui.add_space(6.0);
// Setup spacing between buttons.
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
// Show modal buttons.
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
modal.close();
});
});
columns[1].vertical_centered_justified(|ui| {
View::button(ui, t!("create"), Colors::white_or_black(false), || {
self.account_creating = true;
cb.show_keyboard();
});
});
});
ui.add_space(6.0);
}
}
}
const ACCOUNT_ITEM_HEIGHT: f32 = 75.0;
/// Draw account item.
fn account_item_ui(ui: &mut egui::Ui,
modal: &Modal,
wallet: &mut Wallet,
acc: &WalletAccount,
index: usize,
size: usize) {
// Setup layout size.
let mut rect = ui.available_rect_before_wrap();
rect.set_height(ACCOUNT_ITEM_HEIGHT);
// Draw round background.
let bg_rect = rect.clone();
let item_rounding = View::item_rounding(index, size, false);
ui.painter().rect(bg_rect, item_rounding, Colors::fill(), View::item_stroke());
ui.vertical(|ui| {
ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| {
// Draw button to select account.
let is_current_account = wallet.get_config().account == acc.label;
if !is_current_account {
let button_rounding = View::item_rounding(index, size, true);
View::item_button(ui, button_rounding, CHECK, None, || {
let _ = wallet.set_active_account(&acc.label);
modal.close();
});
} else {
ui.add_space(12.0);
ui.label(RichText::new(CHECK_FAT).size(20.0).color(Colors::green()));
}
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(4.0);
// Show spendable amount.
let amount = amount_to_hr_string(acc.spendable_amount, true);
let amount_text = format!("{} {}", amount, GRIN);
ui.label(RichText::new(amount_text).size(18.0).color(Colors::white_or_black(true)));
ui.add_space(-2.0);
// Show account name.
let default_acc_label = WalletConfig::DEFAULT_ACCOUNT_LABEL.to_string();
let acc_label = if acc.label == default_acc_label {
t!("wallets.default_account")
} else {
acc.label.to_owned()
};
let acc_name = format!("{} {}", FOLDER_USER, acc_label);
View::ellipsize_text(ui, acc_name, 15.0, Colors::text(false));
// Show account BIP32 derivation path.
let acc_path = format!("{} {}", PATH, acc.path);
ui.label(RichText::new(acc_path).size(15.0).color(Colors::gray()));
ui.add_space(3.0);
});
});
});
});
}

View file

@ -20,27 +20,22 @@ use grin_core::core::amount_to_hr_string;
use crate::AppConfig; use crate::AppConfig;
use crate::gui::Colors; use crate::gui::Colors;
use crate::gui::icons::{ARROWS_CLOCKWISE, BRIDGE, CHAT_CIRCLE_TEXT, CHECK, CHECK_FAT, COPY, FOLDER_USER, GEAR_FINE, GRAPH, PACKAGE, PATH, POWER, SCAN, SPINNER, USERS_THREE}; use crate::gui::icons::{ARROWS_CLOCKWISE, BRIDGE, CHAT_CIRCLE_TEXT, COPY, FOLDER_USER, GEAR_FINE, GRAPH, PACKAGE, POWER, SCAN, SPINNER, USERS_THREE};
use crate::gui::platform::PlatformCallbacks; use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{CameraContent, Modal, Content, View}; use crate::gui::views::{CameraContent, Modal, Content, View};
use crate::gui::views::types::{ModalPosition, QrScanResult, TextEditOptions}; use crate::gui::views::types::{ModalPosition, QrScanResult};
use crate::gui::views::wallets::{WalletTransactions, WalletMessages, WalletTransport}; use crate::gui::views::wallets::{WalletTransactions, WalletMessages, WalletTransport};
use crate::gui::views::wallets::types::{GRIN, WalletTab, WalletTabType}; use crate::gui::views::wallets::types::{GRIN, WalletTab, WalletTabType};
use crate::gui::views::wallets::wallet::accounts::WalletAccounts;
use crate::gui::views::wallets::wallet::WalletSettings; use crate::gui::views::wallets::wallet::WalletSettings;
use crate::node::Node; use crate::node::Node;
use crate::wallet::{Wallet, WalletConfig}; use crate::wallet::{Wallet, WalletConfig};
use crate::wallet::types::{WalletAccount, WalletData}; use crate::wallet::types::WalletData;
/// Selected and opened wallet content. /// Selected and opened wallet content.
pub struct WalletContent { pub struct WalletContent {
/// List of wallet accounts for [`Modal`]. /// Wallet accounts [`Modal`] content.
accounts: Vec<WalletAccount>, accounts_modal_content: Option<WalletAccounts>,
/// Flag to check if account is creating.
account_creating: bool,
/// Account label [`Modal`] value.
account_label_edit: String,
/// Flag to check if error occurred during account creation at [`Modal`].
account_creation_error: bool,
/// Camera content for QR scan [`Modal`]. /// Camera content for QR scan [`Modal`].
camera_content: CameraContent, camera_content: CameraContent,
@ -61,10 +56,7 @@ impl WalletContent {
/// Create new instance with optional data. /// Create new instance with optional data.
pub fn new(data: Option<String>) -> Self { pub fn new(data: Option<String>) -> Self {
let mut content = Self { let mut content = Self {
accounts: vec![], accounts_modal_content: None,
account_creating: false,
account_label_edit: "".to_string(),
account_creation_error: false,
camera_content: CameraContent::default(), camera_content: CameraContent::default(),
qr_scan_result: None, qr_scan_result: None,
current_tab: Box::new(WalletTransactions::default()), current_tab: Box::new(WalletTransactions::default()),
@ -195,9 +187,11 @@ impl WalletContent {
Some(id) => { Some(id) => {
match id { match id {
ACCOUNT_LIST_MODAL => { ACCOUNT_LIST_MODAL => {
Modal::ui(ui.ctx(), |ui, modal| { if let Some(content) = self.accounts_modal_content.as_mut() {
self.account_list_modal_ui(ui, wallet, modal, cb); Modal::ui(ui.ctx(), |ui, modal| {
}); content.ui(ui, wallet, modal, cb);
});
}
} }
QR_CODE_SCAN_MODAL => { QR_CODE_SCAN_MODAL => {
Modal::ui(ui.ctx(), |ui, modal| { Modal::ui(ui.ctx(), |ui, modal| {
@ -238,10 +232,7 @@ impl WalletContent {
// Draw button to show list of accounts. // Draw button to show list of accounts.
View::item_button(ui, View::item_rounding(1, 3, true), USERS_THREE, None, || { View::item_button(ui, View::item_rounding(1, 3, true), USERS_THREE, None, || {
// Load accounts. self.accounts_modal_content = Some(WalletAccounts::new(wallet.accounts()));
self.account_label_edit = "".to_string();
self.accounts = wallet.accounts();
self.account_creating = false;
// Show account list modal. // Show account list modal.
Modal::new(ACCOUNT_LIST_MODAL) Modal::new(ACCOUNT_LIST_MODAL)
.position(ModalPosition::CenterTop) .position(ModalPosition::CenterTop)
@ -308,120 +299,6 @@ impl WalletContent {
}); });
} }
/// Draw account list [`Modal`] content.
fn account_list_modal_ui(&mut self,
ui: &mut egui::Ui,
wallet: &mut Wallet,
modal: &Modal,
cb: &dyn PlatformCallbacks) {
if self.account_creating {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("wallets.new_account_desc"))
.size(17.0)
.color(Colors::gray()));
ui.add_space(8.0);
// Draw account name edit.
let text_edit_id = Id::from(modal.id).with(wallet.get_config().id);
let mut text_edit_opts = TextEditOptions::new(text_edit_id);
View::text_edit(ui, cb, &mut self.account_label_edit, &mut text_edit_opts);
// Show error occurred during account creation..
if self.account_creation_error {
ui.add_space(12.0);
ui.label(RichText::new(t!("error"))
.size(17.0)
.color(Colors::red()));
}
ui.add_space(12.0);
});
// Setup spacing between buttons.
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
// Show modal buttons.
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
columns[1].vertical_centered_justified(|ui| {
// Create button callback.
let mut on_create = || {
if !self.account_label_edit.is_empty() {
let label = &self.account_label_edit;
match wallet.create_account(label) {
Ok(_) => {
let _ = wallet.set_active_account(label);
cb.hide_keyboard();
modal.close();
},
Err(_) => self.account_creation_error = true
};
}
};
View::on_enter_key(ui, || {
(on_create)();
});
View::button(ui, t!("create"), Colors::white_or_black(false), on_create);
});
});
ui.add_space(6.0);
} else {
ui.add_space(3.0);
// Show list of accounts.
let size = self.accounts.len();
ScrollArea::vertical()
.id_source("account_list_modal_scroll")
.scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden)
.max_height(266.0)
.auto_shrink([true; 2])
.show_rows(ui, ACCOUNT_ITEM_HEIGHT, size, |ui, row_range| {
for index in row_range {
// Add space before the first item.
if index == 0 {
ui.add_space(4.0);
}
let acc = self.accounts.get(index).unwrap();
account_item_ui(ui, modal, wallet, acc, index, size);
if index == size - 1 {
ui.add_space(4.0);
}
}
});
ui.add_space(2.0);
View::horizontal_line(ui, Colors::stroke());
ui.add_space(6.0);
// Setup spacing between buttons.
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
// Show modal buttons.
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
modal.close();
});
});
columns[1].vertical_centered_justified(|ui| {
View::button(ui, t!("create"), Colors::white_or_black(false), || {
self.account_creating = true;
cb.show_keyboard();
});
});
});
ui.add_space(6.0);
}
}
/// Draw QR code scan [`Modal`] content. /// Draw QR code scan [`Modal`] content.
fn scan_qr_modal_ui(&mut self, fn scan_qr_modal_ui(&mut self,
ui: &mut egui::Ui, ui: &mut egui::Ui,
@ -676,68 +553,4 @@ impl WalletContent {
}); });
}); });
} }
}
const ACCOUNT_ITEM_HEIGHT: f32 = 75.0;
/// Draw account item.
fn account_item_ui(ui: &mut egui::Ui,
modal: &Modal,
wallet: &mut Wallet,
acc: &WalletAccount,
index: usize,
size: usize) {
// Setup layout size.
let mut rect = ui.available_rect_before_wrap();
rect.set_height(ACCOUNT_ITEM_HEIGHT);
// Draw round background.
let bg_rect = rect.clone();
let item_rounding = View::item_rounding(index, size, false);
ui.painter().rect(bg_rect, item_rounding, Colors::fill(), View::item_stroke());
ui.vertical(|ui| {
ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| {
// Draw button to select account.
let is_current_account = wallet.get_config().account == acc.label;
if !is_current_account {
let button_rounding = View::item_rounding(index, size, true);
View::item_button(ui, button_rounding, CHECK, None, || {
let _ = wallet.set_active_account(&acc.label);
modal.close();
});
} else {
ui.add_space(12.0);
ui.label(RichText::new(CHECK_FAT).size(20.0).color(Colors::green()));
}
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(4.0);
// Show spendable amount.
let amount = amount_to_hr_string(acc.spendable_amount, true);
let amount_text = format!("{} {}", amount, GRIN);
ui.label(RichText::new(amount_text).size(18.0).color(Colors::white_or_black(true)));
ui.add_space(-2.0);
// Show account name.
let default_acc_label = WalletConfig::DEFAULT_ACCOUNT_LABEL.to_string();
let acc_label = if acc.label == default_acc_label {
t!("wallets.default_account")
} else {
acc.label.to_owned()
};
let acc_name = format!("{} {}", FOLDER_USER, acc_label);
View::ellipsize_text(ui, acc_name, 15.0, Colors::text(false));
// Show account BIP32 derivation path.
let acc_path = format!("{} {}", PATH, acc.path);
ui.label(RichText::new(acc_path).size(15.0).color(Colors::gray()));
ui.add_space(3.0);
});
});
});
});
} }

View file

@ -27,4 +27,6 @@ mod transport;
pub use transport::WalletTransport; pub use transport::WalletTransport;
mod content; mod content;
pub use content::WalletContent; pub use content::WalletContent;
mod accounts;