ui: content container

This commit is contained in:
ardocrat
2025-05-30 12:33:13 +03:00
parent 5dd8de7950
commit b84f6480e7
34 changed files with 493 additions and 564 deletions
+3 -26
View File
@@ -21,6 +21,7 @@ use crate::gui::views::{Content, KeyboardContent, Modal, TitlePanel, View};
use crate::gui::Colors;
use crate::wallet::ExternalConnection;
use crate::AppConfig;
use crate::gui::views::types::ContentContainer;
/// Implements ui entry point and contains platform-specific callbacks.
pub struct App<Platform> {
@@ -69,9 +70,7 @@ impl<Platform: PlatformCallbacks> App<Platform> {
// Handle Esc keyboard key event.
if ctx.input_mut(|i| i.consume_key(Modifiers::NONE, egui::Key::Escape)) {
if Modal::on_back() {
self.content.on_back(&self.platform);
}
self.content.on_back(&self.platform);
// Request repaint to update previous content.
ctx.request_repaint();
}
@@ -122,13 +121,6 @@ impl<Platform: PlatformCallbacks> App<Platform> {
Self::title_panel_bg(ui, false);
self.content.ui(ui, &self.platform);
}
// Provide incoming data to wallets.
if let Some(data) = crate::consume_incoming_data() {
if !data.is_empty() {
self.content.wallets.on_data(ui, Some(data));
}
}
});
// Check if desktop window was focused after requested attention.
@@ -268,22 +260,7 @@ impl<Platform: PlatformCallbacks> App<Platform> {
}
// Paint the title.
let dual_wallets_panel = ui.available_width() >= (Content::SIDE_PANEL_WIDTH * 3.0) +
View::get_right_inset() + View::get_left_inset();
let wallet_panel_opened = self.content.wallets.showing_wallet();
let show_app_name = if dual_wallets_panel {
wallet_panel_opened && !AppConfig::show_wallets_at_dual_panel()
} else if Content::is_dual_panel_mode(ui.ctx()) {
wallet_panel_opened
} else {
Content::is_network_panel_open() || wallet_panel_opened
};
let creating_wallet = self.content.wallets.creating_wallet();
let title_text = if creating_wallet || show_app_name {
format!("Grim {}", crate::VERSION)
} else {
"".to_string()
};
let title_text = format!("Grim {}", crate::VERSION);
painter.text(
title_rect.center(),
egui::Align2::CENTER_CENTER,
+32 -34
View File
@@ -21,7 +21,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use crate::gui::icons::FILE_X;
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::network::NetworkContent;
use crate::gui::views::types::{ModalContainer, ModalPosition};
use crate::gui::views::types::{ContentContainer, ModalPosition};
use crate::gui::views::wallets::WalletsContent;
use crate::gui::views::{Modal, View};
use crate::gui::Colors;
@@ -39,7 +39,7 @@ pub struct Content {
network: NetworkContent,
/// Central panel [`WalletsContent`] content.
pub wallets: WalletsContent,
wallets: WalletsContent,
/// Check if app exit is allowed on Desktop close event.
pub exit_allowed: bool,
@@ -48,16 +48,8 @@ pub struct Content {
/// Flag to check it's first draw of content.
first_draw: bool,
/// List of allowed [`Modal`] ids for this [`ModalContainer`].
allowed_modal_ids: Vec<&'static str>
}
/// Identifier for integrated node warning [`Modal`] on Android.
const ANDROID_INTEGRATED_NODE_WARNING_MODAL: &'static str = "android_node_warning_modal";
/// Identifier for crash report [`Modal`].
const CRASH_REPORT_MODAL: &'static str = "crash_report_modal";
impl Default for Content {
fn default() -> Self {
// Exit from eframe only for non-mobile platforms.
@@ -69,24 +61,25 @@ impl Default for Content {
exit_allowed,
show_exit_progress: false,
first_draw: true,
allowed_modal_ids: vec![
Self::EXIT_CONFIRMATION_MODAL,
ANDROID_INTEGRATED_NODE_WARNING_MODAL,
CRASH_REPORT_MODAL
],
}
}
}
impl ModalContainer for Content {
fn modal_ids(&self) -> &Vec<&'static str> {
&self.allowed_modal_ids
/// Identifier for integrated node warning [`Modal`] on Android.
const ANDROID_INTEGRATED_NODE_WARNING_MODAL: &'static str = "android_node_warning_modal";
/// Identifier for crash report [`Modal`].
const CRASH_REPORT_MODAL: &'static str = "crash_report_modal";
impl ContentContainer for Content {
fn modal_ids(&self) -> Vec<&'static str> {
vec![
Self::EXIT_CONFIRMATION_MODAL,
ANDROID_INTEGRATED_NODE_WARNING_MODAL,
CRASH_REPORT_MODAL
]
}
fn modal_ui(&mut self,
ui: &mut egui::Ui,
modal: &Modal,
cb: &dyn PlatformCallbacks) {
fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
match modal.id {
Self::EXIT_CONFIRMATION_MODAL => self.exit_modal_content(ui, modal, cb),
ANDROID_INTEGRATED_NODE_WARNING_MODAL => self.android_warning_modal_ui(ui),
@@ -94,12 +87,23 @@ impl ModalContainer for Content {
_ => {}
}
}
fn on_back(&mut self, cb: &dyn PlatformCallbacks) -> bool {
if Modal::on_back() {
if self.wallets.on_back(cb) {
Self::show_exit_modal();
return false;
}
}
true
}
fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
self.content_ui(ui, cb);
}
}
impl Content {
/// Identifier for exit confirmation [`Modal`].
pub const EXIT_CONFIRMATION_MODAL: &'static str = "exit_confirmation_modal";
/// Default width of side panel at application UI.
pub const SIDE_PANEL_WIDTH: f32 = 400.0;
/// Desktop window title height.
@@ -107,9 +111,10 @@ impl Content {
/// Margin of window frame at desktop.
pub const WINDOW_FRAME_MARGIN: f32 = 6.0;
pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
self.current_modal_ui(ui, cb);
/// Identifier for exit confirmation [`Modal`].
pub const EXIT_CONFIRMATION_MODAL: &'static str = "exit_confirmation_modal";
fn content_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
let dual_panel = Self::is_dual_panel_mode(ui.ctx());
let (is_panel_open, mut panel_width) = network_panel_state_width(ui.ctx(), dual_panel);
if self.network.showing_settings() {
@@ -237,13 +242,6 @@ impl Content {
}
}
/// Handle Back key event.
pub fn on_back(&mut self, cb: &dyn PlatformCallbacks) {
if self.wallets.on_back(cb) {
Self::show_exit_modal()
}
}
/// Draw content for integrated node warning [`Modal`] on Android.
fn android_warning_modal_ui(&mut self, ui: &mut egui::Ui) {
ui.add_space(6.0);
+15 -7
View File
@@ -23,6 +23,7 @@ use std::sync::Arc;
use crate::gui::views::types::{ModalPosition, ModalState};
use crate::gui::views::{Content, View};
use crate::gui::Colors;
use crate::gui::platform::PlatformCallbacks;
lazy_static! {
/// Showing [`Modal`] state to be accessible from different ui parts.
@@ -173,8 +174,9 @@ impl Modal {
modal.first_draw.load(Ordering::Relaxed)
}
/// Draw opened [`Modal`] content.
pub fn ui(ctx: &egui::Context, add_content: impl FnOnce(&mut egui::Ui, &Modal)) {
pub fn ui(ctx: &egui::Context,
cb: &dyn PlatformCallbacks,
add_content: impl FnOnce(&mut egui::Ui, &Modal, &dyn PlatformCallbacks)) {
let has_modal = {
MODAL_STATE.read().modal.is_some()
};
@@ -183,12 +185,15 @@ impl Modal {
let r_state = MODAL_STATE.read();
r_state.modal.clone().unwrap()
};
modal.window_ui(ctx, add_content);
modal.window_ui(ctx, cb, add_content);
}
}
/// Draw [`egui::Window`] with provided content.
fn window_ui(&self, ctx: &egui::Context, add_content: impl FnOnce(&mut egui::Ui, &Modal)) {
fn window_ui(&self,
ctx: &egui::Context,
cb: &dyn PlatformCallbacks,
add_content: impl FnOnce(&mut egui::Ui, &Modal, &dyn PlatformCallbacks)) {
let is_fullscreen = ctx.input(|i| {
i.viewport().fullscreen.unwrap_or(false)
});
@@ -249,7 +254,7 @@ impl Modal {
if let Some(title) = &self.title {
title_ui(title, ui);
}
self.content_ui(ui, add_content);
self.content_ui(ui, cb, add_content);
});
// Setup first draw flag.
@@ -289,7 +294,10 @@ impl Modal {
}
/// Draw provided content.
fn content_ui(&self, ui: &mut egui::Ui, add_content: impl FnOnce(&mut egui::Ui, &Modal)) {
fn content_ui(&self,
ui: &mut egui::Ui,
cb: &dyn PlatformCallbacks,
add_content: impl FnOnce(&mut egui::Ui, &Modal, &dyn PlatformCallbacks)) {
let mut rect = ui.available_rect_before_wrap();
// Create background shape.
@@ -308,7 +316,7 @@ impl Modal {
rect.min += egui::emath::vec2(6.0, 0.0);
rect.max -= egui::emath::vec2(6.0, 0.0);
let resp = ui.scope_builder(UiBuilder::new().max_rect(rect), |ui| {
(add_content)(ui, self);
(add_content)(ui, self, cb);
}).response;
// Setup background size.
+12 -15
View File
@@ -21,7 +21,7 @@ use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, View};
use crate::gui::views::network::modals::ExternalConnectionModal;
use crate::gui::views::network::NodeSetup;
use crate::gui::views::types::{ModalContainer, ModalPosition};
use crate::gui::views::types::{ContentContainer, ModalPosition};
use crate::node::{Node, NodeConfig};
use crate::wallet::{ConnectionsConfig, ExternalConnection};
@@ -29,25 +29,21 @@ use crate::wallet::{ConnectionsConfig, ExternalConnection};
pub struct ConnectionsContent {
/// External connection [`Modal`] content.
ext_conn_modal: ExternalConnectionModal,
/// [`Modal`] identifiers allowed at this ui container.
modal_ids: Vec<&'static str>
}
impl Default for ConnectionsContent {
fn default() -> Self {
Self {
ext_conn_modal: ExternalConnectionModal::new(None),
modal_ids: vec![
ExternalConnectionModal::NETWORK_ID
],
}
}
}
impl ModalContainer for ConnectionsContent {
fn modal_ids(&self) -> &Vec<&'static str> {
&self.modal_ids
impl ContentContainer for ConnectionsContent {
fn modal_ids(&self) -> Vec<&'static str> {
vec![
ExternalConnectionModal::NETWORK_ID
]
}
fn modal_ui(&mut self,
@@ -61,13 +57,12 @@ impl ModalContainer for ConnectionsContent {
_ => {}
}
}
}
impl ConnectionsContent {
/// Draw connections content.
pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
self.current_modal_ui(ui, cb);
fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool {
true
}
fn container_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) {
ui.add_space(2.0);
// Show network type selection.
@@ -121,7 +116,9 @@ impl ConnectionsContent {
}
}
}
}
impl ConnectionsContent {
/// Draw integrated node connection item content.
pub fn integrated_node_item_ui(ui: &mut egui::Ui, custom_button: impl FnOnce(&mut egui::Ui)) {
// Draw round background.
+4 -4
View File
@@ -23,7 +23,7 @@ use crate::gui::views::{Content, TitlePanel, View};
use crate::gui::views::network::{ConnectionsContent, NetworkMetrics, NetworkMining, NetworkNode, NetworkSettings};
use crate::gui::views::network::types::{NodeTab, NodeTabType};
use crate::gui::views::settings::SettingsContent;
use crate::gui::views::types::{LinePosition, TitleContentType, TitleType};
use crate::gui::views::types::{ContentContainer, LinePosition, TitleContentType, TitleType};
use crate::node::{Node, NodeConfig, NodeError};
use crate::wallet::ExternalConnection;
@@ -111,7 +111,7 @@ impl NetworkContent {
let rect = ui.available_rect_before_wrap();
if let Some(c) = &mut self.settings_content {
View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| {
c.ui(ui);
c.ui(ui, cb);
});
} else if self.node_tab_content.get_type() != NodeTabType::Settings {
View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| {
@@ -124,11 +124,11 @@ impl NetworkContent {
Node::is_stopping() {
NetworkContent::loading_ui(ui, None);
} else {
self.node_tab_content.ui(ui, cb);
self.node_tab_content.tab_ui(ui, cb);
}
});
} else {
self.node_tab_content.ui(ui, cb);
self.node_tab_content.tab_ui(ui, cb);
}
// Draw content divider line.
+1 -1
View File
@@ -38,7 +38,7 @@ impl NodeTab for NetworkMetrics {
NodeTabType::Metrics
}
fn ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) {
fn tab_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) {
let server_stats = Node::get_stats();
let stats = server_stats.as_ref().unwrap();
if stats.diff_stats.height == 0 {
+2 -1
View File
@@ -24,6 +24,7 @@ use crate::gui::views::{Content, View};
use crate::gui::views::network::NetworkContent;
use crate::gui::views::network::setup::StratumSetup;
use crate::gui::views::network::types::{NodeTab, NodeTabType};
use crate::gui::views::types::ContentContainer;
use crate::node::{Node, NodeConfig};
/// Mining tab content.
@@ -45,7 +46,7 @@ impl NodeTab for NetworkMining {
NodeTabType::Mining
}
fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn tab_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
if Node::is_stratum_starting() || Node::get_sync_status().unwrap() != SyncStatus::NoSync {
NetworkContent::loading_ui(ui, Some(t!("network_mining.loading")));
return;
+7 -1
View File
@@ -22,6 +22,9 @@ use crate::wallet::{ConnectionsConfig, ExternalConnection};
/// Content to create or update external wallet connection.
pub struct ExternalConnectionModal {
/// Flag to check if content was just rendered.
first_draw: bool,
/// External connection URL value for [`Modal`].
ext_node_url_edit: String,
/// External connection API secret value for [`Modal`].
@@ -46,6 +49,7 @@ impl ExternalConnectionModal {
("".to_string(), "".to_string(), None)
};
Self {
first_draw: true,
ext_node_url_edit,
ext_node_secret_edit,
ext_node_url_error: false,
@@ -103,7 +107,7 @@ impl ExternalConnectionModal {
let url_edit_id = Id::from(modal.id).with(self.ext_conn_id).with("node_url");
let mut url_edit = TextEdit::new(url_edit_id)
.paste()
.focus(Modal::first_draw());
.focus(self.first_draw);
url_edit.ui(ui, &mut self.ext_node_url_edit, cb);
ui.add_space(8.0);
@@ -163,5 +167,7 @@ impl ExternalConnectionModal {
});
ui.add_space(6.0);
});
self.first_draw = false;
}
}
+1 -1
View File
@@ -32,7 +32,7 @@ impl NodeTab for NetworkNode {
NodeTabType::Info
}
fn ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) {
fn tab_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) {
ScrollArea::vertical()
.id_salt("integrated_node_info_scroll")
.scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden)
+19 -18
View File
@@ -21,7 +21,7 @@ use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, Content, View};
use crate::gui::views::network::setup::{DandelionSetup, NodeSetup, P2PSetup, PoolSetup, StratumSetup};
use crate::gui::views::network::types::{NodeTab, NodeTabType};
use crate::gui::views::types::{ModalContainer, ModalPosition};
use crate::gui::views::types::{ContentContainer, ModalPosition};
use crate::node::{Node, NodeConfig};
/// Integrated node settings tab content.
@@ -36,9 +36,6 @@ pub struct NetworkSettings {
pool: PoolSetup,
/// Dandelion server setup content.
dandelion: DandelionSetup,
/// [`Modal`] identifiers allowed at this ui container.
modal_ids: Vec<&'static str>
}
/// Identifier for settings reset confirmation [`Modal`].
@@ -52,16 +49,15 @@ impl Default for NetworkSettings {
stratum: StratumSetup::default(),
pool: PoolSetup::default(),
dandelion: DandelionSetup::default(),
modal_ids: vec![
RESET_SETTINGS_CONFIRMATION_MODAL
]
}
}
}
impl ModalContainer for NetworkSettings {
fn modal_ids(&self) -> &Vec<&'static str> {
&self.modal_ids
impl ContentContainer for NetworkSettings {
fn modal_ids(&self) -> Vec<&'static str> {
vec![
RESET_SETTINGS_CONFIRMATION_MODAL
]
}
fn modal_ui(&mut self,
@@ -73,17 +69,12 @@ impl ModalContainer for NetworkSettings {
_ => {}
}
}
}
impl NodeTab for NetworkSettings {
fn get_type(&self) -> NodeTabType {
NodeTabType::Settings
fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool {
true
}
fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
// Draw modal content for current ui container.
self.current_modal_ui(ui, cb);
fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
ScrollArea::vertical()
.id_salt("node_settings_scroll")
.scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden)
@@ -135,6 +126,16 @@ impl NodeTab for NetworkSettings {
}
}
impl NodeTab for NetworkSettings {
fn get_type(&self) -> NodeTabType {
NodeTabType::Settings
}
fn tab_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
self.ui(ui, cb);
}
}
impl NetworkSettings {
/// Reminder to restart enabled node to show on edit setting at [`Modal`].
pub fn node_restart_required_ui(ui: &mut egui::Ui) {
+15 -18
View File
@@ -16,7 +16,7 @@ use egui::{Id, RichText};
use crate::gui::icons::{CLOCK_COUNTDOWN, GRAPH, TIMER, WATCH};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::types::{ModalContainer, ModalPosition};
use crate::gui::views::types::{ContentContainer, ModalPosition};
use crate::gui::views::{Modal, TextEdit, View};
use crate::gui::Colors;
use crate::gui::views::network::NetworkSettings;
@@ -35,9 +35,6 @@ pub struct DandelionSetup {
/// Stem phase probability value (stem 90% of the time, fluff 10% of the time by default).
stem_prob_edit: String,
/// [`Modal`] identifiers allowed at this ui container.
modal_ids: Vec<&'static str>,
}
/// Identifier epoch duration value [`Modal`].
@@ -56,19 +53,18 @@ impl Default for DandelionSetup {
embargo_edit: NodeConfig::get_reorg_cache_period(),
aggregation_edit: NodeConfig::get_dandelion_aggregation(),
stem_prob_edit: NodeConfig::get_stem_probability(),
modal_ids: vec![
EPOCH_MODAL,
EMBARGO_MODAL,
AGGREGATION_MODAL,
STEM_PROBABILITY_MODAL
]
}
}
}
impl ModalContainer for DandelionSetup {
fn modal_ids(&self) -> &Vec<&'static str> {
&self.modal_ids
impl ContentContainer for DandelionSetup {
fn modal_ids(&self) -> Vec<&'static str> {
vec![
EPOCH_MODAL,
EMBARGO_MODAL,
AGGREGATION_MODAL,
STEM_PROBABILITY_MODAL
]
}
fn modal_ui(&mut self,
@@ -83,13 +79,12 @@ impl ModalContainer for DandelionSetup {
_ => {}
}
}
}
impl DandelionSetup {
pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
// Draw modal content for current ui container.
self.current_modal_ui(ui, cb);
fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool {
true
}
fn container_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) {
View::sub_title(ui, format!("{} {}", GRAPH, "Dandelion"));
View::horizontal_line(ui, Colors::stroke());
ui.add_space(6.0);
@@ -131,7 +126,9 @@ impl DandelionSetup {
ui.add_space(6.0);
});
}
}
impl DandelionSetup {
/// Draw epoch duration setup content.
fn epoch_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.epoch_duration"))
+15 -18
View File
@@ -19,7 +19,7 @@ use crate::gui::icons::{CLOCK_CLOCKWISE, COMPUTER_TOWER, PLUG, POWER, SHIELD, SH
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::network::settings::NetworkSettings;
use crate::gui::views::network::NetworkContent;
use crate::gui::views::types::{ModalContainer, ModalPosition};
use crate::gui::views::types::{ContentContainer, ModalPosition};
use crate::gui::views::{Modal, TextEdit, View};
use crate::gui::Colors;
use crate::node::{Node, NodeConfig};
@@ -43,9 +43,6 @@ pub struct NodeSetup {
/// Future Time Limit value.
ftl_edit: String,
/// [`Modal`] identifiers allowed at this ui container.
modal_ids: Vec<&'static str>
}
/// Identifier for API port value [`Modal`].
@@ -68,19 +65,18 @@ impl Default for NodeSetup {
is_api_port_available,
secret_edit: "".to_string(),
ftl_edit: NodeConfig::get_ftl(),
modal_ids: vec![
API_PORT_MODAL,
API_SECRET_MODAL,
FOREIGN_API_SECRET_MODAL,
FTL_MODAL
]
}
}
}
impl ModalContainer for NodeSetup {
fn modal_ids(&self) -> &Vec<&'static str> {
&self.modal_ids
impl ContentContainer for NodeSetup {
fn modal_ids(&self) -> Vec<&'static str> {
vec![
API_PORT_MODAL,
API_SECRET_MODAL,
FOREIGN_API_SECRET_MODAL,
FTL_MODAL
]
}
fn modal_ui(&mut self,
@@ -95,13 +91,12 @@ impl ModalContainer for NodeSetup {
_ => {}
}
}
}
impl NodeSetup {
pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
// Draw modal content for current ui container.
self.current_modal_ui(ui, cb);
fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool {
true
}
fn container_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) {
View::sub_title(ui, format!("{} {}", COMPUTER_TOWER, t!("network_settings.server")));
View::horizontal_line(ui, Colors::stroke());
ui.add_space(6.0);
@@ -218,7 +213,9 @@ impl NodeSetup {
self.archive_mode_ui(ui);
});
}
}
impl NodeSetup {
/// Draw [`ChainTypes`] setup content.
pub fn chain_type_ui(ui: &mut egui::Ui) {
ui.vertical_centered(|ui| {
+24 -28
View File
@@ -21,7 +21,7 @@ use crate::gui::icons::{ARROW_FAT_LINES_DOWN, ARROW_FAT_LINES_UP, GLOBE_SIMPLE,
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, TextEdit, View};
use crate::gui::views::network::settings::NetworkSettings;
use crate::gui::views::types::{ModalContainer, ModalPosition};
use crate::gui::views::types::{ContentContainer, ModalPosition};
use crate::node::{Node, NodeConfig, PeersConfig};
/// Type of peer.
@@ -65,9 +65,6 @@ pub struct P2PSetup {
/// Flag to check if reset of peers was called.
peers_reset: bool,
/// [`Modal`] identifiers allowed at this ui container.
modal_ids: Vec<&'static str>
}
/// Identifier for port value [`Modal`].
@@ -111,23 +108,22 @@ impl Default for P2PSetup {
max_inbound_count: NodeConfig::get_max_inbound_peers(),
max_outbound_count: NodeConfig::get_max_outbound_peers(),
peers_reset: false,
modal_ids: vec![
PORT_MODAL,
CUSTOM_SEED_MODAL,
ALLOW_PEER_MODAL,
DENY_PEER_MODAL,
PREFER_PEER_MODAL,
BAN_WINDOW_MODAL,
MAX_INBOUND_MODAL,
MAX_OUTBOUND_MODAL
]
}
}
}
impl ModalContainer for P2PSetup {
fn modal_ids(&self) -> &Vec<&'static str> {
&self.modal_ids
impl ContentContainer for P2PSetup {
fn modal_ids(&self) -> Vec<&'static str> {
vec![
PORT_MODAL,
CUSTOM_SEED_MODAL,
ALLOW_PEER_MODAL,
DENY_PEER_MODAL,
PREFER_PEER_MODAL,
BAN_WINDOW_MODAL,
MAX_INBOUND_MODAL,
MAX_OUTBOUND_MODAL
]
}
fn modal_ui(&mut self,
@@ -146,16 +142,12 @@ impl ModalContainer for P2PSetup {
_ => {}
}
}
}
impl P2PSetup {
/// Title for custom DNS Seeds setup section.
const DNS_SEEDS_TITLE: &'static str = "DNS Seeds";
pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
// Draw modal content for current ui container.
self.current_modal_ui(ui, cb);
fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool {
true
}
fn container_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) {
View::sub_title(ui, format!("{} {}", HANDSHAKE, t!("network_settings.p2p_server")));
View::horizontal_line(ui, Colors::stroke());
ui.add_space(6.0);
@@ -236,7 +228,12 @@ impl P2PSetup {
}
});
}
}
/// Title for custom DNS Seeds setup section.
const DNS_SEEDS_TITLE: &'static str = "DNS Seeds";
impl P2PSetup {
/// Draw p2p port setup content.
fn port_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.p2p_port"))
@@ -398,7 +395,7 @@ impl P2PSetup {
PeerType::Allowed => t!("network_settings.allow_list"),
PeerType::Denied => t!("network_settings.deny_list"),
PeerType::Preferred => t!("network_settings.favourites"),
_ => Self::DNS_SEEDS_TITLE.to_string()
_ => DNS_SEEDS_TITLE.to_string()
};
// Show modal to add peer.
Modal::new(modal_id)
@@ -483,8 +480,7 @@ impl P2PSetup {
/// Draw seeding type setup content.
fn seeding_type_ui(&mut self, ui: &mut egui::Ui) {
let title = Self::DNS_SEEDS_TITLE;
ui.label(RichText::new(title).size(16.0).color(Colors::gray()));
ui.label(RichText::new(DNS_SEEDS_TITLE).size(16.0).color(Colors::gray()));
ui.add_space(2.0);
let default_seeding = NodeConfig::is_default_seeding_type();
+21 -28
View File
@@ -19,40 +19,33 @@ use crate::gui::icons::{BEZIER_CURVE, BOUNDING_BOX, CHART_SCATTER, CIRCLES_THREE
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, TextEdit, View};
use crate::gui::views::network::settings::NetworkSettings;
use crate::gui::views::types::{ModalContainer, ModalPosition};
use crate::gui::views::types::{ContentContainer, ModalPosition};
use crate::node::NodeConfig;
/// Memory pool setup section content.
pub struct PoolSetup {
/// Base fee value that's accepted into the pool.
fee_base_edit: String,
/// Reorg cache retention period value in minutes.
reorg_period_edit: String,
/// Maximum number of transactions allowed in the pool.
pool_size_edit: String,
/// Maximum number of transactions allowed in the stempool.
stempool_size_edit: String,
/// Maximum total weight of transactions to build a block.
max_weight_edit: String,
/// [`Modal`] identifiers allowed at this ui container.
modal_ids: Vec<&'static str>,
}
/// Identifier for base fee value [`Modal`].
pub const FEE_BASE_MODAL: &'static str = "fee_base";
const FEE_BASE_MODAL: &'static str = "fee_base";
/// Identifier for reorg cache retention period value [`Modal`].
pub const REORG_PERIOD_MODAL: &'static str = "reorg_period";
const REORG_PERIOD_MODAL: &'static str = "reorg_period";
/// Identifier for maximum number of transactions in the pool [`Modal`].
pub const POOL_SIZE_MODAL: &'static str = "pool_size";
const POOL_SIZE_MODAL: &'static str = "pool_size";
/// Identifier for maximum number of transactions in the stempool [`Modal`].
pub const STEMPOOL_SIZE_MODAL: &'static str = "stempool_size";
const STEMPOOL_SIZE_MODAL: &'static str = "stempool_size";
/// Identifier for maximum total weight of transactions [`Modal`].
pub const MAX_WEIGHT_MODAL: &'static str = "max_weight";
const MAX_WEIGHT_MODAL: &'static str = "max_weight";
impl Default for PoolSetup {
fn default() -> Self {
@@ -62,20 +55,19 @@ impl Default for PoolSetup {
pool_size_edit: NodeConfig::get_max_pool_size(),
stempool_size_edit: NodeConfig::get_max_stempool_size(),
max_weight_edit: NodeConfig::get_mineable_max_weight(),
modal_ids: vec![
FEE_BASE_MODAL,
REORG_PERIOD_MODAL,
POOL_SIZE_MODAL,
STEMPOOL_SIZE_MODAL,
MAX_WEIGHT_MODAL
]
}
}
}
impl ModalContainer for PoolSetup {
fn modal_ids(&self) -> &Vec<&'static str> {
&self.modal_ids
impl ContentContainer for PoolSetup {
fn modal_ids(&self) -> Vec<&'static str> {
vec![
FEE_BASE_MODAL,
REORG_PERIOD_MODAL,
POOL_SIZE_MODAL,
STEMPOOL_SIZE_MODAL,
MAX_WEIGHT_MODAL
]
}
fn modal_ui(&mut self,
@@ -91,13 +83,12 @@ impl ModalContainer for PoolSetup {
_ => {}
}
}
}
impl PoolSetup {
pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
// Draw modal content for current ui container.
self.current_modal_ui(ui, cb);
fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool {
true
}
fn container_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) {
View::sub_title(ui, format!("{} {}", CHART_SCATTER, t!("network_settings.tx_pool")));
View::horizontal_line(ui, Colors::stroke());
ui.add_space(6.0);
@@ -135,7 +126,9 @@ impl PoolSetup {
self.max_weight_ui(ui);
});
}
}
impl PoolSetup {
/// Draw fee base setup content.
fn fee_base_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.pool_fee"))
+18 -21
View File
@@ -20,7 +20,7 @@ use crate::gui::icons::{BARBELL, HARD_DRIVES, PLUG, POWER, TIMER};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, TextEdit, View};
use crate::gui::views::network::settings::NetworkSettings;
use crate::gui::views::types::{ModalContainer, ModalPosition};
use crate::gui::views::types::{ContentContainer, ModalPosition};
use crate::gui::views::wallets::modals::WalletsModal;
use crate::node::{Node, NodeConfig};
use crate::wallet::{WalletConfig, WalletList};
@@ -51,9 +51,6 @@ pub struct StratumSetup {
/// Minimum share difficulty value to request from miners.
min_share_diff_edit: String,
/// [`Modal`] identifiers allowed at this ui container.
modal_ids: Vec<&'static str>
}
/// Identifier for wallet selection [`Modal`].
@@ -91,19 +88,18 @@ impl Default for StratumSetup {
wallet_name,
attempt_time_edit: NodeConfig::get_stratum_attempt_time(),
min_share_diff_edit: NodeConfig::get_stratum_min_share_diff(),
modal_ids: vec![
WALLET_SELECTION_MODAL,
STRATUM_PORT_MODAL,
ATTEMPT_TIME_MODAL,
MIN_SHARE_DIFF_MODAL
]
}
}
}
impl ModalContainer for StratumSetup {
fn modal_ids(&self) -> &Vec<&'static str> {
&self.modal_ids
impl ContentContainer for StratumSetup {
fn modal_ids(&self) -> Vec<&'static str> {
vec![
WALLET_SELECTION_MODAL,
STRATUM_PORT_MODAL,
ATTEMPT_TIME_MODAL,
MIN_SHARE_DIFF_MODAL
]
}
fn modal_ui(&mut self,
@@ -112,7 +108,7 @@ impl ModalContainer for StratumSetup {
cb: &dyn PlatformCallbacks) {
match modal.id {
WALLET_SELECTION_MODAL => {
self.wallets_modal.ui(ui, modal, &mut self.wallets, cb, |wallet, _| {
self.wallets_modal.ui(ui, &mut self.wallets, |wallet, _| {
let id = wallet.get_config().id;
NodeConfig::save_stratum_wallet_id(id);
self.wallet_name = WalletConfig::read_name_by_id(id);
@@ -124,13 +120,12 @@ impl ModalContainer for StratumSetup {
_ => {}
}
}
}
impl StratumSetup {
pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
// Draw modal content for current ui container.
self.current_modal_ui(ui, cb);
fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool {
true
}
fn container_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) {
View::sub_title(ui, format!("{} {}", HARD_DRIVES, t!("network_mining.server")));
View::horizontal_line(ui, Colors::stroke());
ui.add_space(6.0);
@@ -192,8 +187,8 @@ impl StratumSetup {
View::button(ui,
t!("network_settings.choose_wallet"),
Colors::white_or_black(false), || {
self.show_wallets_modal();
});
self.show_wallets_modal();
});
ui.add_space(12.0);
if self.wallet_name.is_some() {
@@ -242,7 +237,9 @@ impl StratumSetup {
self.min_diff_ui(ui);
});
}
}
impl StratumSetup {
/// Show wallet selection [`Modal`].
fn show_wallets_modal(&mut self) {
self.wallets_modal = WalletsModal::new(NodeConfig::get_stratum_wallet_id(), None, false);
+1 -1
View File
@@ -17,7 +17,7 @@ use crate::gui::platform::PlatformCallbacks;
/// Integrated node tab content interface.
pub trait NodeTab {
fn get_type(&self) -> NodeTabType;
fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks);
fn tab_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks);
}
/// Type of [`NodeTab`] content.
+17 -3
View File
@@ -18,7 +18,9 @@ use egui::{Layout, RichText};
use crate::AppConfig;
use crate::gui::Colors;
use crate::gui::icons::{CHECK, CHECK_FAT};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, View};
use crate::gui::views::types::ContentContainer;
/// Application settings content.
pub struct SettingsContent {
@@ -34,9 +36,19 @@ impl Default for SettingsContent {
}
}
impl SettingsContent {
/// Draw settings content.
pub fn ui(&mut self, ui: &mut egui::Ui) {
impl ContentContainer for SettingsContent {
fn modal_ids(&self) -> Vec<&'static str> {
vec![]
}
fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
}
fn on_back(&mut self, cb: &dyn PlatformCallbacks) -> bool {
true
}
fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
ui.add_space(6.0);
// Show theme selection.
@@ -60,7 +72,9 @@ impl SettingsContent {
Self::language_item_ui(locale, ui, index, locales.len());
}
}
}
impl SettingsContent {
/// Draw theme selection content.
fn theme_selection_ui(ui: &mut egui::Ui) {
ui.vertical_centered(|ui| {
+19 -20
View File
@@ -51,28 +51,27 @@ pub struct ModalState {
pub modal: Option<Modal>,
}
/// Contains identifiers to draw opened [`Modal`] content for current ui container.
pub trait ModalContainer {
/// Content container to simplify modals management and navigation.
pub trait ContentContainer {
/// List of allowed [`Modal`] identifiers.
fn modal_ids(&self) -> &Vec<&'static str>;
/// Draw modal ui content.
fn modal_ui(&mut self,
ui: &mut egui::Ui,
modal: &Modal,
cb: &dyn PlatformCallbacks);
/// Draw [`Modal`] for current ui container if it's possible.
fn current_modal_ui(&mut self,
ui: &mut egui::Ui,
cb: &dyn PlatformCallbacks) {
let modal_id = Modal::opened();
let draw = modal_id.is_some() && self.modal_ids().contains(&modal_id.unwrap());
if draw {
Modal::ui(ui.ctx(), |ui, modal| {
self.modal_ui(ui, modal, cb);
});
fn modal_ids(&self) -> Vec<&'static str>;
/// Draw modal content.
fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks);
/// Handle back key action, return `false` when consumed inside container.
fn on_back(&mut self, cb: &dyn PlatformCallbacks) -> bool;
/// Draw container content.
fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks);
/// Draw content, to call by parent container.
fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
// Draw modal content.
if let Some(id) = Modal::opened() {
if self.modal_ids().contains(&id) {
Modal::ui(ui.ctx(), cb, |ui, modal, cb| {
self.modal_ui(ui, modal, cb);
});
}
}
self.container_ui(ui, cb);
}
}
+112 -126
View File
@@ -12,24 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::time::Duration;
use egui::{Align, CornerRadius, Id, Layout, Margin, RichText, ScrollArea, StrokeKind};
use egui::scroll_area::ScrollBarVisibility;
use egui::{Align, CornerRadius, Id, Layout, Margin, RichText, ScrollArea, StrokeKind};
use std::time::Duration;
use crate::AppConfig;
use crate::gui::Colors;
use crate::gui::icons::{ARROW_LEFT, CARET_RIGHT, COMPUTER_TOWER, FOLDER_OPEN, FOLDER_PLUS, GEAR, GLOBE, GLOBE_SIMPLE, LOCK_KEY, PLUS, SIDEBAR_SIMPLE, SUITCASE};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, Content, TitlePanel, View};
use crate::gui::views::settings::SettingsContent;
use crate::gui::views::types::{ModalContainer, ModalPosition, LinePosition, TitleContentType, TitleType};
use crate::gui::views::wallets::creation::WalletCreation;
use crate::gui::views::types::{ContentContainer, LinePosition, ModalPosition, TitleContentType, TitleType};
use crate::gui::views::wallets::creation::WalletCreationContent;
use crate::gui::views::wallets::modals::{AddWalletModal, OpenWalletModal, WalletConnectionModal, WalletsModal};
use crate::gui::views::wallets::types::WalletTabType;
use crate::gui::views::wallets::wallet::types::wallet_status_text;
use crate::gui::views::wallets::WalletContent;
use crate::wallet::{ExternalConnection, Wallet, WalletList};
use crate::gui::views::{Content, Modal, TitlePanel, View};
use crate::gui::Colors;
use crate::wallet::types::ConnectionMethod;
use crate::wallet::{ExternalConnection, Wallet, WalletList};
use crate::AppConfig;
/// Wallets content.
pub struct WalletsContent {
@@ -37,27 +37,21 @@ pub struct WalletsContent {
wallets: WalletList,
/// Initial wallet creation [`Modal`] content.
add_wallet_modal_content: Option<AddWalletModal>,
add_wallet_modal_content: AddWalletModal,
/// Wallet opening [`Modal`] content.
open_wallet_content: Option<OpenWalletModal>,
/// Wallet connection selection content.
conn_selection_content: Option<WalletConnectionModal>,
open_wallet_content: OpenWalletModal,
/// Wallet connection selection [`Modal`] content.
conn_selection_content: WalletConnectionModal,
/// Wallet selection [`Modal`] content.
wallet_selection_content: Option<WalletsModal>,
wallet_selection_content: WalletsModal,
/// Selected [`Wallet`] content.
wallet_content: Option<WalletContent>,
/// Wallet creation content.
creation_content: Option<WalletCreation>,
creation_content: Option<WalletCreationContent>,
/// Settings content.
settings_content: Option<SettingsContent>,
/// Flag to show [`Wallet`] list at dual panel mode.
show_wallets_at_dual_panel: bool,
/// [`Modal`] identifiers allowed at this ui container.
modal_ids: Vec<&'static str>
}
const ADD_WALLET_MODAL: &'static str = "wallets_add_modal";
@@ -69,97 +63,121 @@ impl Default for WalletsContent {
fn default() -> Self {
Self {
wallets: WalletList::default(),
wallet_selection_content: None,
open_wallet_content: None,
conn_selection_content: None,
wallet_selection_content: WalletsModal::new(None, None, true),
open_wallet_content: OpenWalletModal::new(None),
add_wallet_modal_content: AddWalletModal::default(),
conn_selection_content: WalletConnectionModal::new(ConnectionMethod::Integrated),
wallet_content: None,
creation_content: None,
settings_content: None,
show_wallets_at_dual_panel: AppConfig::show_wallets_at_dual_panel(),
modal_ids: vec![
ADD_WALLET_MODAL,
OPEN_WALLET_MODAL,
SELECT_CONNECTION_MODAL,
SELECT_WALLET_MODAL,
],
add_wallet_modal_content: None,
}
}
}
impl ModalContainer for WalletsContent {
fn modal_ids(&self) -> &Vec<&'static str> {
&self.modal_ids
impl ContentContainer for WalletsContent {
fn modal_ids(&self) -> Vec<&'static str> {
vec![
ADD_WALLET_MODAL,
OPEN_WALLET_MODAL,
SELECT_CONNECTION_MODAL,
SELECT_WALLET_MODAL,
]
}
fn modal_ui(&mut self,
ui: &mut egui::Ui,
modal: &Modal,
cb: &dyn PlatformCallbacks) {
fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
match modal.id {
ADD_WALLET_MODAL => {
if let Some(content) = self.add_wallet_modal_content.as_mut() {
content.ui(ui, modal, cb, |name, pass| {
self.creation_content = Some(
WalletCreation::new(name.clone(), pass.clone())
);
});
}
if self.creating_wallet() {
self.add_wallet_modal_content = None;
}
self.add_wallet_modal_content.ui(ui, modal, cb, |name, pass| {
self.creation_content = Some(
WalletCreationContent::new(name.clone(), pass.clone())
);
});
},
OPEN_WALLET_MODAL => {
let mut open = false;
if let Some(open_content) = self.open_wallet_content.as_mut() {
open_content.ui(ui, modal, cb, |wallet, data| {
self.wallet_content = Some(WalletContent::new(wallet, data));
open = true;
});
}
if open {
self.open_wallet_content = None;
}
self.open_wallet_content.ui(ui, modal, cb, |pass, _| {
if let Some(content) = &self.wallet_content {
return match content.wallet.open(pass) {
Ok(_) => true,
Err(_) => false
};
}
true
});
},
SELECT_CONNECTION_MODAL => {
if let Some(content) = self.conn_selection_content.as_mut() {
content.ui(ui, modal, cb, |conn| {
if let Some(wallet_content) = &self.wallet_content {
wallet_content.wallet.update_connection(&conn);
}
});
}
self.conn_selection_content.ui(ui, modal, cb, |conn| {
if let Some(wallet_content) = &self.wallet_content {
wallet_content.wallet.update_connection(&conn);
}
});
}
SELECT_WALLET_MODAL => {
let mut select = false;
if let Some(content) = self.wallet_selection_content.as_mut() {
content.ui(ui, modal, &mut self.wallets, cb, |wallet, data| {
self.wallet_content = Some(WalletContent::new(wallet, data));
select = true;
});
}
if select {
self.wallet_selection_content = None;
}
self.wallet_selection_content.ui(ui, &mut self.wallets, |wallet, data| {
if !wallet.is_open() {
self.open_wallet_content = OpenWalletModal::new(data.clone());
Modal::new(OPEN_WALLET_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("wallets.open"))
.show();
}
self.wallet_content = Some(WalletContent::new(wallet, data));
});
}
_ => {}
}
}
fn on_back(&mut self, cb: &dyn PlatformCallbacks) -> bool {
if self.showing_settings() {
// Close settings.
self.settings_content = None;
return false;
} else if self.creating_wallet() {
// Close wallet creation.
let creation = self.creation_content.as_mut().unwrap();
if creation.on_back() {
self.creation_content = None;
}
return false;
} else {
if self.showing_wallet() {
let content = self.wallet_content.as_mut().unwrap();
// Close opened QR code scanner.
if content.qr_scan_content.is_some() {
cb.stop_camera();
content.qr_scan_content = None;
return false;
}
// Close opened wallet.
self.wallet_content = None;
return false;
}
}
true
}
fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
self.content_ui(ui, cb);
}
}
impl WalletsContent {
/// Draw wallets content.
pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
self.current_modal_ui(ui, cb);
fn content_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
if let Some(data) = crate::consume_incoming_data() {
if !data.is_empty() {
self.on_data(ui, Some(data));
}
}
let showing_settings = self.showing_settings();
let creating_wallet = self.creating_wallet();
let showing_wallet = self.showing_wallet() && !creating_wallet && !showing_settings;
let dual_panel = Self::is_dual_panel_mode(ui);
let dual_panel = is_dual_panel_mode(ui);
let content_width = ui.available_width();
let list_hidden = showing_settings || creating_wallet || self.wallets.list().is_empty()
|| (showing_wallet && self.wallet_content.as_ref().unwrap().qr_scan_content.is_some())
|| (dual_panel && showing_wallet && !self.show_wallets_at_dual_panel)
|| (dual_panel && showing_wallet && !AppConfig::show_wallets_at_dual_panel())
|| (!dual_panel && showing_wallet);
// Show title panel.
@@ -262,7 +280,7 @@ impl WalletsContent {
if self.showing_settings() {
if let Some(c) = &mut self.settings_content {
View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| {
c.ui(ui);
c.ui(ui, cb);
});
}
} else if self.creating_wallet() {
@@ -270,7 +288,7 @@ impl WalletsContent {
let pass = creation.pass.clone();
let mut created = false;
// Show wallet creation content.
creation.ui(ui, cb, |wallet| {
creation.content_ui(ui, cb, |wallet| {
self.wallets.add(wallet.clone());
if let Ok(_) = wallet.open(pass.clone()) {
self.wallet_content = Some(WalletContent::new(wallet, None));
@@ -324,7 +342,7 @@ impl WalletsContent {
}
/// Handle data from deeplink or opened file.
pub fn on_data(&mut self, ui: &mut egui::Ui, data: Option<String>) {
fn on_data(&mut self, ui: &mut egui::Ui, data: Option<String>) {
let wallets_size = self.wallets.list().len();
if wallets_size == 0 {
return;
@@ -348,7 +366,7 @@ impl WalletsContent {
/// Show initial wallet creation [`Modal`].
pub fn show_add_wallet_modal(&mut self) {
self.add_wallet_modal_content = Some(AddWalletModal::default());
self.add_wallet_modal_content = AddWalletModal::default();
Modal::new(ADD_WALLET_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("wallets.add"))
@@ -357,50 +375,20 @@ impl WalletsContent {
/// Show wallet selection with provided optional data.
fn show_wallet_selection_modal(&mut self, data: Option<String>) {
self.wallet_selection_content = Some(WalletsModal::new(None, data, true));
self.wallet_selection_content = WalletsModal::new(None, data, true);
Modal::new(SELECT_WALLET_MODAL)
.position(ModalPosition::Center)
.title(t!("network_settings.choose_wallet"))
.show();
}
/// Handle Back key event returning `false` when event was handled.
pub fn on_back(&mut self, cb: &dyn PlatformCallbacks) -> bool {
if self.showing_settings() {
// Close settings.
self.settings_content = None;
return false;
} else if self.creating_wallet() {
// Close wallet creation.
let creation = self.creation_content.as_mut().unwrap();
if creation.on_back() {
self.creation_content = None;
}
return false;
} else {
if self.showing_wallet() {
let content = self.wallet_content.as_mut().unwrap();
// Close opened QR code scanner.
if content.qr_scan_content.is_some() {
cb.stop_camera();
content.qr_scan_content = None;
return false;
}
// Close opened wallet.
self.wallet_content = None;
return false;
}
}
true
}
/// Draw [`TitlePanel`] content.
fn title_ui(&mut self,
ui: &mut egui::Ui,
dual_panel: bool,
show_wallet: bool,
cb: &dyn PlatformCallbacks) {
let show_list = self.show_wallets_at_dual_panel;
let show_list = AppConfig::show_wallets_at_dual_panel();
let showing_settings = self.showing_settings();
let creating_wallet = self.creating_wallet();
let qr_scan = {
@@ -501,7 +489,6 @@ impl WalletsContent {
SUITCASE
};
View::title_button_big(ui, list_icon, |_| {
self.show_wallets_at_dual_panel = !show_list;
AppConfig::toggle_show_wallets_at_dual_panel();
});
}
@@ -586,9 +573,8 @@ impl WalletsContent {
let mut show_selection = false;
View::item_button(ui, CornerRadius::default(), GLOBE, None, || {
self.wallet_content = Some(WalletContent::new(wallet.clone(), None));
self.conn_selection_content = Some(
WalletConnectionModal::new(wallet.get_current_connection())
);
self.conn_selection_content =
WalletConnectionModal::new(wallet.get_current_connection());
// Show connection selection modal.
Modal::new(SELECT_CONNECTION_MODAL)
.position(ModalPosition::CenterTop)
@@ -657,17 +643,17 @@ impl WalletsContent {
/// Show [`Modal`] to select and open wallet.
fn show_opening_modal(&mut self, wallet: Wallet, data: Option<String>) {
self.wallet_content = Some(WalletContent::new(wallet.clone(), None));
self.open_wallet_content = Some(OpenWalletModal::new(wallet, data));
self.open_wallet_content = OpenWalletModal::new(data);
Modal::new(OPEN_WALLET_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("wallets.open"))
.show();
}
}
/// Check if it's possible to show [`WalletsContent`] and [`WalletContent`] panels at same time.
fn is_dual_panel_mode(ui: &mut egui::Ui) -> bool {
let dual_panel_root = Content::is_dual_panel_mode(ui.ctx());
let max_width = ui.available_width();
dual_panel_root && max_width >= (Content::SIDE_PANEL_WIDTH * 2.0) + View::get_right_inset()
}
/// Check if it's possible to show [`WalletsContent`] and [`WalletContent`] panels at same time.
fn is_dual_panel_mode(ui: &mut egui::Ui) -> bool {
let dual_panel_root = Content::is_dual_panel_mode(ui.ctx());
let max_width = ui.available_width();
dual_panel_root && max_width >= (Content::SIDE_PANEL_WIDTH * 2.0) + View::get_right_inset()
}
@@ -20,7 +20,7 @@ use crate::gui::Colors;
use crate::gui::icons::{CHECK, CLIPBOARD_TEXT, COPY, SCAN};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, Content, View, CameraScanModal};
use crate::gui::views::types::{LinePosition, ModalContainer, ModalPosition, QrScanResult};
use crate::gui::views::types::{LinePosition, ContentContainer, ModalPosition, QrScanResult};
use crate::gui::views::wallets::creation::MnemonicSetup;
use crate::gui::views::wallets::creation::types::Step;
use crate::gui::views::wallets::ConnectionSettings;
@@ -29,7 +29,7 @@ use crate::wallet::{ExternalConnection, Wallet};
use crate::wallet::types::PhraseMode;
/// Wallet creation content.
pub struct WalletCreation {
pub struct WalletCreationContent {
/// Wallet name.
pub name: String,
/// Wallet password.
@@ -48,16 +48,15 @@ pub struct WalletCreation {
/// Flag to check if an error occurred during wallet creation.
creation_error: Option<String>,
/// [`Modal`] identifiers allowed at this ui container.
modal_ids: Vec<&'static str>
}
const QR_CODE_PHRASE_SCAN_MODAL: &'static str = "qr_code_rec_phrase_scan_modal";
impl ModalContainer for WalletCreation {
fn modal_ids(&self) -> &Vec<&'static str> {
&self.modal_ids
impl ContentContainer for WalletCreationContent {
fn modal_ids(&self) -> Vec<&'static str> {
vec![
QR_CODE_PHRASE_SCAN_MODAL
]
}
fn modal_ui(&mut self,
@@ -86,10 +85,16 @@ impl ModalContainer for WalletCreation {
_ => {}
}
}
fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool {
true
}
fn container_ui(&mut self, _: &mut egui::Ui, _: &dyn PlatformCallbacks) {}
}
impl WalletCreation {
/// Create new wallet creation instance from name and password.
impl WalletCreationContent {
/// Create new wallet creation content from name and password.
pub fn new(name: String, pass: ZeroingString) -> Self {
Self {
name,
@@ -99,19 +104,15 @@ impl WalletCreation {
mnemonic_setup: MnemonicSetup::default(),
network_setup: ConnectionSettings::default(),
creation_error: None,
modal_ids: vec![
QR_CODE_PHRASE_SCAN_MODAL
],
}
}
/// Draw wallet creation content.
pub fn ui(&mut self,
ui: &mut egui::Ui,
cb: &dyn PlatformCallbacks,
on_create: impl FnMut(Wallet)) {
self.current_modal_ui(ui, cb);
pub fn content_ui(&mut self,
ui: &mut egui::Ui,
cb: &dyn PlatformCallbacks,
on_create: impl FnMut(Wallet)) {
self.ui(ui, cb);
egui::TopBottomPanel::bottom("wallet_creation_step_panel")
.frame(egui::Frame {
inner_margin: Margin {
@@ -350,14 +351,14 @@ impl WalletCreation {
/// Draw wallet creation [`Step`] content.
fn step_content_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
match &self.step {
Step::EnterMnemonic => self.mnemonic_setup.ui(ui, cb),
Step::EnterMnemonic => self.mnemonic_setup.import_ui(ui, cb),
Step::ConfirmMnemonic => self.mnemonic_setup.confirm_ui(ui, cb),
Step::SetupConnection => {
// Redraw if node is running.
if Node::is_running() && !Content::is_dual_panel_mode(ui.ctx()) {
ui.ctx().request_repaint_after(Node::STATS_UPDATE_DELAY);
}
self.network_setup.create_ui(ui, cb)
self.network_setup.content_ui(ui, cb);
}
}
}
+16 -19
View File
@@ -18,7 +18,7 @@ use crate::gui::Colors;
use crate::gui::icons::PENCIL;
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, Content, View, TextEdit};
use crate::gui::views::types::{ModalContainer, ModalPosition};
use crate::gui::views::types::{ContentContainer, ModalPosition};
use crate::wallet::Mnemonic;
use crate::wallet::types::{PhraseMode, PhraseSize, PhraseWord};
@@ -33,9 +33,6 @@ pub struct MnemonicSetup {
word_edit: String,
/// Flag to check if entered word is valid at [`Modal`].
valid_word_edit: bool,
/// [`Modal`] identifiers allowed at this ui container.
modal_ids: Vec<&'static str>
}
/// Identifier for word input [`Modal`].
@@ -48,16 +45,15 @@ impl Default for MnemonicSetup {
word_index_edit: 0,
word_edit: String::from(""),
valid_word_edit: true,
modal_ids: vec![
WORD_INPUT_MODAL
]
}
}
}
impl ModalContainer for MnemonicSetup {
fn modal_ids(&self) -> &Vec<&'static str> {
&self.modal_ids
impl ContentContainer for MnemonicSetup {
fn modal_ids(&self) -> Vec<&'static str> {
vec![
WORD_INPUT_MODAL
]
}
fn modal_ui(&mut self,
@@ -69,14 +65,19 @@ impl ModalContainer for MnemonicSetup {
_ => {}
}
}
fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool {
true
}
fn container_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) {
}
}
impl MnemonicSetup {
/// Draw content for phrase input step.
pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
// Draw modal content for current ui container.
self.current_modal_ui(ui, cb);
/// Draw content for phrase import step.
pub fn import_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
ui.add_space(10.0);
// Show mode and type setup.
@@ -89,12 +90,8 @@ impl MnemonicSetup {
// Show words setup.
self.word_list_ui(ui, self.mnemonic.mode() == PhraseMode::Import);
}
/// Draw content for phrase confirmation step.
pub fn confirm_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
// Draw modal content for current ui container.
self.current_modal_ui(ui, cb);
ui.add_space(4.0);
ui.vertical_centered(|ui| {
let text = format!("{}:", t!("wallets.recovery_phrase"));
+2 -2
View File
@@ -15,7 +15,7 @@
mod mnemonic;
pub use mnemonic::MnemonicSetup;
mod creation;
pub use creation::WalletCreation;
mod content;
pub use content::WalletCreationContent;
pub mod types;
+6 -14
View File
@@ -18,13 +18,9 @@ use grin_util::ZeroingString;
use crate::gui::Colors;
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, TextEdit, View};
use crate::wallet::Wallet;
/// Wallet opening [`Modal`] content.
pub struct OpenWalletModal {
/// Wallet to open.
wallet: Wallet,
/// Password to open wallet.
pass_edit: String,
/// Flag to check if wrong password was entered.
@@ -36,9 +32,8 @@ pub struct OpenWalletModal {
impl OpenWalletModal {
/// Create new content instance.
pub fn new(wallet: Wallet, data: Option<String>) -> Self {
pub fn new(data: Option<String>) -> Self {
Self {
wallet,
pass_edit: "".to_string(),
wrong_pass: false,
data,
@@ -49,20 +44,17 @@ impl OpenWalletModal {
ui: &mut egui::Ui,
modal: &Modal,
cb: &dyn PlatformCallbacks,
mut on_continue: impl FnMut(Wallet, Option<String>)) {
mut on_continue: impl FnMut(ZeroingString, &Option<String>) -> bool) {
// Callback for button to continue.
let mut on_continue = |m: &mut OpenWalletModal| {
let pass = m.pass_edit.clone();
if pass.is_empty() {
return;
}
match m.wallet.open(ZeroingString::from(pass)) {
Ok(_) => {
m.pass_edit = "".to_string();
Modal::close();
on_continue(m.wallet.clone(), m.data.clone());
}
Err(_) => m.wrong_pass = true
m.wrong_pass = !on_continue(ZeroingString::from(pass), &m.data);
if !m.wrong_pass {
m.pass_edit = "".to_string();
Modal::close();
}
};
+5 -28
View File
@@ -15,15 +15,12 @@
use egui::scroll_area::ScrollBarVisibility;
use egui::{Align, Layout, RichText, ScrollArea, StrokeKind};
use crate::gui::Colors;
use crate::gui::icons::{CHECK, CHECK_FAT, COMPUTER_TOWER, FOLDER_OPEN, GLOBE_SIMPLE, PLUGS_CONNECTED};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, View};
use crate::gui::views::types::ModalPosition;
use crate::gui::views::wallets::modals::OpenWalletModal;
use crate::gui::views::wallets::wallet::types::wallet_status_text;
use crate::wallet::{Wallet, WalletList};
use crate::gui::views::{Modal, View};
use crate::gui::Colors;
use crate::wallet::types::ConnectionMethod;
use crate::wallet::{Wallet, WalletList};
/// Wallet list [`Modal`] content
pub struct WalletsModal {
@@ -35,32 +32,19 @@ pub struct WalletsModal {
/// Flag to check if wallet can be opened from the list.
can_open: bool,
/// Wallet opening content.
open_wallet_content: Option<OpenWalletModal>,
}
impl WalletsModal {
/// Create new content instance.
pub fn new(selected_id: Option<i64>, data: Option<String>, can_open: bool) -> Self {
Self { selected_id, data, can_open, open_wallet_content: None }
Self { selected_id, data, can_open }
}
/// Draw content.
pub fn ui(&mut self,
ui: &mut egui::Ui,
modal: &Modal,
wallets: &WalletList,
cb: &dyn PlatformCallbacks,
mut on_select: impl FnMut(Wallet, Option<String>)) {
// Draw wallet opening modal content.
if let Some(open_content) = self.open_wallet_content.as_mut() {
open_content.ui(ui, modal, cb, |wallet, data| {
on_select(wallet, data);
self.data = None;
});
return;
}
ui.add_space(4.0);
ScrollArea::vertical()
.max_height(373.0)
@@ -123,14 +107,7 @@ impl WalletsModal {
FOLDER_OPEN
};
View::item_button(ui, View::item_rounding(0, 1, true), icon, None, || {
if wallet.is_open() {
on_select();
} else {
Modal::change_position(ModalPosition::CenterTop);
self.open_wallet_content = Some(
OpenWalletModal::new(wallet.clone(), self.data.clone())
);
}
on_select();
});
} else {
// Draw button to select wallet.
+17 -21
View File
@@ -23,7 +23,7 @@ use crate::gui::Colors;
use crate::gui::icons::{ARROWS_CLOCKWISE, BRIDGE, CAMERA_ROTATE, CHAT_CIRCLE_TEXT, FOLDER_USER, GEAR_FINE, GRAPH, PACKAGE, POWER, SCAN, SPINNER, USERS_THREE};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, Content, View, CameraContent};
use crate::gui::views::types::{LinePosition, ModalContainer, ModalPosition, QrScanResult};
use crate::gui::views::types::{LinePosition, ContentContainer, ModalPosition, QrScanResult};
use crate::gui::views::wallets::{WalletTransactions, WalletMessages, WalletTransport};
use crate::gui::views::wallets::types::{GRIN, WalletTab, WalletTabType};
use crate::gui::views::wallets::wallet::modals::WalletAccountsModal;
@@ -40,48 +40,47 @@ pub struct WalletContent {
pub current_tab: Box<dyn WalletTab>,
/// Wallet accounts [`Modal`] content.
accounts_modal_content: Option<WalletAccountsModal>,
accounts_modal_content: WalletAccountsModal,
/// QR code scan content.
pub qr_scan_content: Option<CameraContent>,
/// List of allowed [`Modal`] ids for this [`ModalContainer`].
allowed_modal_ids: Vec<&'static str>
}
/// Identifier for account list [`Modal`].
const ACCOUNT_LIST_MODAL: &'static str = "account_list_modal";
impl ModalContainer for WalletContent {
fn modal_ids(&self) -> &Vec<&'static str> {
&self.allowed_modal_ids
impl ContentContainer for WalletContent {
fn modal_ids(&self) -> Vec<&'static str> {
vec![
ACCOUNT_LIST_MODAL,
]
}
fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
match modal.id {
ACCOUNT_LIST_MODAL => {
if let Some(content) = self.accounts_modal_content.as_mut() {
Modal::ui(ui.ctx(), |ui, modal| {
content.ui(ui, &self.wallet, modal, cb);
});
}
self.accounts_modal_content.ui(ui, &self.wallet, modal, cb);
}
_ => {}
}
}
fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool {
true
}
fn container_ui(&mut self, _: &mut egui::Ui, _: &dyn PlatformCallbacks) {}
}
impl WalletContent {
/// Create new instance with optional data.
pub fn new(wallet: Wallet, data: Option<String>) -> Self {
let accounts_modal = WalletAccountsModal::new(wallet.accounts());
let mut content = Self {
wallet,
accounts_modal_content: None,
accounts_modal_content: accounts_modal,
qr_scan_content: None,
current_tab: Box::new(WalletTransactions::default()),
allowed_modal_ids: vec![
ACCOUNT_LIST_MODAL,
],
};
if data.is_some() {
content.on_data(data);
@@ -97,7 +96,6 @@ impl WalletContent {
/// Draw wallet content.
pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
ui.ctx().request_repaint_after(Duration::from_millis(1000));
self.current_modal_ui(ui, cb);
let dual_panel = Content::is_dual_panel_mode(ui.ctx());
let show_wallets_dual = AppConfig::show_wallets_at_dual_panel();
@@ -302,9 +300,7 @@ impl WalletContent {
// Draw button to show list of accounts.
View::item_button(ui, View::item_rounding(1, 3, true), USERS_THREE, None, || {
self.accounts_modal_content = Some(
WalletAccountsModal::new(self.wallet.accounts())
);
self.accounts_modal_content = WalletAccountsModal::new(self.wallet.accounts());
Modal::new(ACCOUNT_LIST_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("wallets.accounts"))
@@ -37,10 +37,10 @@ pub struct WalletMessages {
first_draw: bool,
/// Invoice or sending request creation [`Modal`] content.
request_modal_content: Option<MessageRequestModal>,
request_modal_content: MessageRequestModal,
/// Wallet transaction [`Modal`] content.
tx_info_content: Option<WalletTransactionModal>,
tx_info_content: WalletTransactionModal,
/// Slatepacks message input text.
message_edit: String,
@@ -85,8 +85,8 @@ impl WalletMessages {
message_loading: false,
message_error: "".to_string(),
message_result: Arc::new(Default::default()),
tx_info_content: None,
request_modal_content: None,
tx_info_content: WalletTransactionModal::new(None, false),
request_modal_content: MessageRequestModal::new(false),
file_pick_button: FilePickButton::default(),
scan_modal_content: None,
}
@@ -129,23 +129,19 @@ impl WalletMessages {
Some(id) => {
match id {
REQUEST_MODAL => {
if let Some(content) = self.request_modal_content.as_mut() {
Modal::ui(ui.ctx(), |ui, modal| {
content.ui(ui, wallet, modal, cb);
});
}
Modal::ui(ui.ctx(), cb, |ui, modal, cb| {
self.request_modal_content.ui(ui, wallet, modal, cb);
});
}
TX_INFO_MODAL => {
if let Some(content) = self.tx_info_content.as_mut() {
Modal::ui(ui.ctx(), |ui, modal| {
content.ui(ui, wallet, modal, cb);
});
}
Modal::ui(ui.ctx(), cb, |ui, modal, cb| {
self.tx_info_content.ui(ui, wallet, modal, cb);
});
}
SCAN_QR_MODAL => {
let mut result = None;
if let Some(content) = self.scan_modal_content.as_mut() {
Modal::ui(ui.ctx(), |ui, _| {
Modal::ui(ui.ctx(), cb, |ui, _, cb| {
content.ui(ui, cb, |res| {
result = Some(res.clone());
Modal::close();
@@ -217,7 +213,7 @@ impl WalletMessages {
/// Show [`Modal`] to create invoice or sending request.
fn show_request_modal(&mut self, invoice: bool) {
self.request_modal_content = Some(MessageRequestModal::new(invoice));
self.request_modal_content = MessageRequestModal::new(invoice);
let title = if invoice {
t!("wallets.receive")
} else {
@@ -299,7 +295,7 @@ impl WalletMessages {
Ok(tx) => {
self.message_edit.clear();
// Show transaction modal on success.
self.tx_info_content = Some(WalletTransactionModal::new(wallet, tx, false));
self.tx_info_content = WalletTransactionModal::new(Some(tx.data.id), false);
Modal::new(TX_INFO_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("wallets.tx"))
@@ -322,9 +318,8 @@ impl WalletMessages {
// Show tx modal or show default error message.
if let Some(tx) = wallet.tx_by_slate(&slate).as_ref() {
self.message_edit.clear();
self.tx_info_content = Some(
WalletTransactionModal::new(wallet, tx, false)
);
self.tx_info_content =
WalletTransactionModal::new(Some(tx.data.id), false);
Modal::new(TX_INFO_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("wallets.tx"))
@@ -427,7 +422,7 @@ impl WalletMessages {
if exists {
if let Some(tx) = wallet.tx_by_slate(&slate).as_ref() {
self.message_edit.clear();
self.tx_info_content = Some(WalletTransactionModal::new(wallet, tx, false));
self.tx_info_content = WalletTransactionModal::new(Some(tx.data.id), false);
Modal::new(TX_INFO_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("wallets.tx"))
@@ -229,7 +229,8 @@ impl MessageRequestModal {
let result = r_request.as_ref().unwrap();
match result {
Ok(tx) => {
self.result_tx_content = Some(WalletTransactionModal::new(wallet, tx, false));
self.result_tx_content =
Some(WalletTransactionModal::new(Some(tx.data.id), false));
}
Err(err) => {
match err {
@@ -146,17 +146,17 @@ impl CommonSettings {
Some(id) => {
match id {
NAME_EDIT_MODAL => {
Modal::ui(ui.ctx(), |ui, modal| {
Modal::ui(ui.ctx(), cb, |ui, modal, cb| {
self.name_modal_ui(ui, wallet, modal, cb);
});
}
PASS_EDIT_MODAL => {
Modal::ui(ui.ctx(), |ui, modal| {
Modal::ui(ui.ctx(), cb, |ui, modal, cb| {
self.pass_modal_ui(ui, wallet, modal, cb);
});
}
MIN_CONFIRMATIONS_EDIT_MODAL => {
Modal::ui(ui.ctx(), |ui, modal| {
Modal::ui(ui.ctx(), cb, |ui, modal, cb| {
self.min_conf_modal_ui(ui, wallet, modal, cb);
});
}
@@ -20,7 +20,7 @@ use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Modal, View};
use crate::gui::views::network::ConnectionsContent;
use crate::gui::views::network::modals::ExternalConnectionModal;
use crate::gui::views::types::{ModalContainer, ModalPosition};
use crate::gui::views::types::{ContentContainer, ModalPosition};
use crate::wallet::{ConnectionsConfig, ExternalConnection, Wallet};
use crate::wallet::types::ConnectionMethod;
@@ -31,9 +31,6 @@ pub struct ConnectionSettings {
/// External connection [`Modal`] content.
ext_conn_modal: ExternalConnectionModal,
/// [`Modal`] identifiers allowed at this ui container.
modal_ids: Vec<&'static str>
}
impl Default for ConnectionSettings {
@@ -41,16 +38,15 @@ impl Default for ConnectionSettings {
Self {
method: ConnectionMethod::Integrated,
ext_conn_modal: ExternalConnectionModal::new(None),
modal_ids: vec![
ExternalConnectionModal::WALLET_ID
]
}
}
}
impl ModalContainer for ConnectionSettings {
fn modal_ids(&self) -> &Vec<&'static str> {
&self.modal_ids
impl ContentContainer for ConnectionSettings {
fn modal_ids(&self) -> Vec<&'static str> {
vec![
ExternalConnectionModal::WALLET_ID
]
}
fn modal_ui(&mut self,
@@ -64,20 +60,21 @@ impl ModalContainer for ConnectionSettings {
_ => {}
}
}
fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool {
true
}
fn container_ui(&mut self, _: &mut egui::Ui, _: &dyn PlatformCallbacks) {}
}
impl ConnectionSettings {
/// Draw wallet creation setup content.
pub fn create_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
self.ui(ui, cb);
}
/// Draw existing wallet connection setup content.
pub fn wallet_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) {
self.method = wallet.get_current_connection();
// Draw setup content.
let changed = self.ui(ui, cb);
let changed = self.content_ui(ui, cb);
if changed {
wallet.update_connection(&self.method);
@@ -90,10 +87,9 @@ impl ConnectionSettings {
}
/// Draw connection setup content, returning `true` if connection was changed.
fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) -> bool {
pub fn content_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) -> bool {
self.ui(ui, cb);
let mut changed = false;
// Draw modal content for current ui container.
self.current_modal_ui(ui, cb);
ui.add_space(2.0);
View::sub_title(ui, format!("{} {}", GLOBE, t!("wallets.conn_method")));
@@ -148,12 +148,12 @@ impl RecoverySettings {
Some(id) => {
match id {
RECOVERY_PHRASE_MODAL => {
Modal::ui(ui.ctx(), |ui, modal| {
Modal::ui(ui.ctx(), cb, |ui, modal, cb| {
self.recovery_phrase_modal_ui(ui, wallet, modal, cb);
});
}
DELETE_CONFIRMATION_MODAL => {
Modal::ui(ui.ctx(), |ui, _| {
Modal::ui(ui.ctx(), cb, |ui, _, _| {
self.deletion_modal_ui(ui, wallet);
});
}
@@ -96,20 +96,20 @@ impl WalletTransport {
match id {
SEND_TOR_MODAL => {
if let Some(content) = self.send_modal_content.as_mut() {
Modal::ui(ui.ctx(), |ui, modal| {
Modal::ui(ui.ctx(), cb, |ui, modal, cb| {
content.ui(ui, wallet, modal, cb);
});
}
}
TOR_SETTINGS_MODAL => {
if let Some(content) = self.settings_modal_content.as_mut() {
Modal::ui(ui.ctx(), |ui, modal| {
Modal::ui(ui.ctx(), cb, |ui, modal, cb| {
content.ui(ui, wallet, modal, cb);
});
}
}
QR_ADDRESS_MODAL => {
Modal::ui(ui.ctx(), |ui, _| {
Modal::ui(ui.ctx(), cb, |ui, _, cb| {
self.qr_address_modal_ui(ui, cb);
});
}
@@ -342,7 +342,7 @@ impl TransportSendModal {
match res {
Ok(tx) => {
self.tx_info_content =
Some(WalletTransactionModal::new(wallet, &tx, false));
Some(WalletTransactionModal::new(Some(tx.data.id), false));
}
Err(_) => {
self.error = true;
+6 -6
View File
@@ -165,7 +165,7 @@ impl WalletTransactions {
r.nw = 0.0 as u8;
r.sw = 0.0 as u8;
View::item_button(ui, r, FILE_TEXT, None, || {
self.show_tx_info_modal(wallet, tx, false);
self.show_tx_info_modal(tx, false);
});
}
@@ -175,7 +175,7 @@ impl WalletTransactions {
if wallet_loaded && tx.can_finalize {
let (icon, color) = (CHECK, Some(Colors::green()));
View::item_button(ui, CornerRadius::default(), icon, color, || {
self.show_tx_info_modal(wallet, tx, true);
self.show_tx_info_modal(tx, true);
});
}
@@ -246,14 +246,14 @@ impl WalletTransactions {
Some(id) => {
match id {
TX_INFO_MODAL => {
Modal::ui(ui.ctx(), |ui, 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(), |ui, _| {
Modal::ui(ui.ctx(), cb, |ui, _, cb| {
self.cancel_confirmation_modal(ui, wallet);
});
}
@@ -453,8 +453,8 @@ impl WalletTransactions {
}
/// Show transaction information [`Modal`].
fn show_tx_info_modal(&mut self, wallet: &Wallet, tx: &WalletTransaction, finalize: bool) {
let modal = WalletTransactionModal::new(wallet, tx, finalize);
fn show_tx_info_modal(&mut self, tx: &WalletTransaction, finalize: bool) {
let modal = WalletTransactionModal::new(Some(tx.data.id), finalize);
self.tx_info_content = Some(modal);
Modal::new(TX_INFO_MODAL)
.position(ModalPosition::CenterTop)
+39 -32
View File
@@ -36,7 +36,7 @@ pub struct WalletTransactionModal {
tx_id: u32,
/// Response Slatepack message input value.
response_edit: String,
response_edit: Option<String>,
/// Flag to show transaction finalization input.
show_finalization: bool,
@@ -61,32 +61,10 @@ pub struct WalletTransactionModal {
impl WalletTransactionModal {
/// Create new content instance with [`Wallet`] from provided [`WalletTransaction`].
pub fn new(wallet: &Wallet, tx: &WalletTransaction, show_finalization: bool) -> Self {
pub fn new(tx_id: Option<u32>, show_finalization: bool) -> Self {
Self {
tx_id: tx.data.id,
response_edit: if !tx.cancelling && !tx.finalizing && !tx.data.confirmed &&
tx.data.tx_slate_id.is_some() &&
(tx.data.tx_type == TxLogEntryType::TxSent ||
tx.data.tx_type == TxLogEntryType::TxReceived) {
let mut slate = Slate::blank(1, false);
slate.state = if tx.can_finalize {
if tx.data.tx_type == TxLogEntryType::TxSent {
SlateState::Standard1
} else {
SlateState::Invoice1
}
} else {
if tx.data.tx_type == TxLogEntryType::TxReceived {
SlateState::Standard2
} else {
SlateState::Invoice2
}
};
slate.id = tx.data.tx_slate_id.unwrap();
wallet.read_slatepack(&slate).unwrap_or("".to_string())
} else {
"".to_string()
},
tx_id: tx_id.unwrap(),
response_edit: None,
finalize_edit: "".to_string(),
finalize_error: false,
show_finalization,
@@ -121,13 +99,42 @@ impl WalletTransactionModal {
}
let tx = txs.get(0).unwrap();
// Check if response was loaded.
if self.response_edit.is_none() {
self.response_edit = Some(
if !tx.cancelling && !tx.finalizing && !tx.data.confirmed &&
tx.data.tx_slate_id.is_some() &&
(tx.data.tx_type == TxLogEntryType::TxSent ||
tx.data.tx_type == TxLogEntryType::TxReceived) {
let mut slate = Slate::blank(1, false);
slate.state = if tx.can_finalize {
if tx.data.tx_type == TxLogEntryType::TxSent {
SlateState::Standard1
} else {
SlateState::Invoice1
}
} else {
if tx.data.tx_type == TxLogEntryType::TxReceived {
SlateState::Standard2
} else {
SlateState::Invoice2
}
};
slate.id = tx.data.tx_slate_id.unwrap();
wallet.read_slatepack(&slate).unwrap_or("".to_string())
} else {
"".to_string()
}
);
}
// Show transaction information.
if self.qr_code_content.is_none() && self.scan_qr_content.is_none() {
self.info_ui(ui, tx, wallet, cb);
}
// Show Slatepack message interaction.
if !self.response_edit.is_empty() {
if !self.response_edit.as_ref().unwrap().is_empty() {
self.message_ui(ui, tx, wallet, modal, cb);
}
@@ -201,7 +208,7 @@ impl WalletTransactionModal {
if let Ok(_) = res {
self.show_finalization = false;
self.finalize_edit = "".to_string();
self.response_edit = "".to_string();
self.response_edit = Some("".to_string());
} else {
self.finalize_error = true;
}
@@ -370,7 +377,7 @@ impl WalletTransactionModal {
let message_edit = if self.show_finalization {
&mut self.finalize_edit
} else {
&mut self.response_edit
&mut self.response_edit.as_mut().unwrap()
};
let message_before = message_edit.clone();
@@ -471,14 +478,14 @@ impl WalletTransactionModal {
let qr_text = format!("{} {}", QR_CODE, t!("qr_code"));
View::button(ui, qr_text.clone(), Colors::white_or_black(false), || {
let text = self.response_edit.clone();
self.qr_code_content = Some(QrCodeContent::new(text, true));
self.qr_code_content = Some(QrCodeContent::new(text.unwrap(), true));
});
});
columns[1].vertical_centered_justified(|ui| {
// Draw copy button.
let copy_text = format!("{} {}", COPY, t!("copy"));
View::button(ui, copy_text, Colors::white_or_black(false), || {
cb.copy_string_to_buffer(self.response_edit.clone());
cb.copy_string_to_buffer(self.response_edit.clone().unwrap());
self.finalize_edit = "".to_string();
if tx.can_finalize {
self.show_finalization = true;
@@ -499,7 +506,7 @@ impl WalletTransactionModal {
Colors::white_or_black(false), || {
if let Some((s, _)) = wallet.read_slate_by_tx(tx) {
let name = format!("{}.{}.slatepack", s.id, s.state);
let data = self.response_edit.as_bytes().to_vec();
let data = self.response_edit.as_ref().unwrap().as_bytes().to_vec();
cb.share_data(name, data).unwrap_or_default();
}
});