settings: network proxy

This commit is contained in:
ardocrat
2025-05-31 14:12:31 +03:00
parent e5548eb6f1
commit fd14700eae
11 changed files with 358 additions and 26 deletions
+4
View File
@@ -29,6 +29,7 @@ choose_file: Datei auswählen
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
wallets:
await_conf_amount: Erwarte Bestätigung
await_fin_amount: Warten auf die Fertigstellung
@@ -292,6 +293,9 @@ modal:
modal_exit:
description: Sind Sie sicher, dass Sie die Anwendung beenden wollen?
exit: Schließen
app_settings:
proxy: Proxy
proxy_desc: Lohnt es sich, einen Proxy für Netzwerkanfragen von der Anwendung zu verwenden.
keyboard:
1: 1
2: 2
+4
View File
@@ -29,6 +29,7 @@ choose_file: Choose file
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
wallets:
await_conf_amount: Awaiting confirmation
await_fin_amount: Awaiting finalization
@@ -292,6 +293,9 @@ modal:
modal_exit:
description: Are you sure you want to quit the application?
exit: Exit
app_settings:
proxy: Proxy
proxy_desc: Whether to use proxy for network requests from the application.
keyboard:
1: 1
2: 2
+4
View File
@@ -29,6 +29,7 @@ choose_file: Choisir un fichier
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
wallets:
await_conf_amount: En attente de confirmation
await_fin_amount: En attente de finalisation
@@ -292,6 +293,9 @@ modal:
modal_exit:
description: "Êtes-vous sûr de vouloir quitter l'application ?"
exit: Quitter
app_settings:
proxy: Proxy
proxy_desc: Vaut-il la peine d'utiliser un proxy pour les requêtes réseau de l'application.
keyboard:
1: 1
2: 2
+4
View File
@@ -29,6 +29,7 @@ choose_file: Выбрать файл
crash_report: Отчёт о сбое
crash_report_warning: В прошлый раз приложение неожиданно закрылось, вы можете поделиться отчетом о сбое с разработчиками.
confirmation: Подтверждение
enter_url: Введите URL-адрес
wallets:
await_conf_amount: Ожидает подтверждения
await_fin_amount: Ожидает завершения
@@ -292,6 +293,9 @@ modal:
modal_exit:
description: Вы уверены, что хотите выйти из приложения?
exit: Выход
app_settings:
proxy: Прокси
proxy_desc: Стоит ли использовать прокси для сетевых запросов из приложения.
keyboard:
1: 1
2: 2
+4
View File
@@ -29,6 +29,7 @@ choose_file: Dosya seçin
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
wallets:
await_conf_amount: Onay bekleniyor
await_fin_amount: Tamamlanma bekleniyor
@@ -292,6 +293,9 @@ modal:
modal_exit:
description: Uygulamadan cikmak için exit, emin misiniz?
exit: Exit
app_settings:
proxy: Proxy
proxy_desc: Uygulamadan gelen ağ istekleri için bir proxy kullanmaya değer mi.
keyboard:
1: 1
2: 2
+4
View File
@@ -29,6 +29,7 @@ choose_file: 选择文件
crash_report: 崩溃报告
crash_report_warning: 上次应用程序意外关闭,您可以报告开发人员崩溃事件.
confirmation: 确认
enter_url: 输入 URL
wallets:
await_conf_amount: 等待确认中
await_fin_amount: 等待确定中
@@ -292,6 +293,9 @@ modal:
modal_exit:
description: 您确定要退出应用程序吗?
exit: 退出手
app_settings:
proxy: 代理
proxy_desc: 是否值得对来自应用程序的网络请求使用代理.
keyboard:
1: 1
2: 2
+17 -4
View File
@@ -12,13 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::gui::Colors;
use crate::gui::icons::{DATABASE, GLOBE_SIMPLE};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::settings::interface::InterfaceSettingsContent;
use crate::gui::views::settings::network::NetworkSettingsContent;
use crate::gui::views::settings::storage::StorageSettingsContent;
use crate::gui::views::types::ContentContainer;
use crate::gui::views::View;
use crate::gui::Colors;
/// Application settings content.
pub struct SettingsContent {
@@ -49,11 +50,23 @@ impl SettingsContent {
ui.add_space(8.0);
View::horizontal_line(ui, Colors::stroke());
ui.add_space(6.0);
View::sub_title(ui, format!("{} {}", GLOBE_SIMPLE, t!("network.self")));
View::horizontal_line(ui, Colors::stroke());
ui.add_space(6.0);
// Show network settings.
//self.network_settings.ui(ui, cb);
self.network_settings.ui(ui, cb);
ui.add_space(8.0);
View::horizontal_line(ui, Colors::stroke());
ui.add_space(6.0);
// View::sub_title(ui, format!("{} {}", DATABASE, t!("network_node.data")));
// View::horizontal_line(ui, Colors::stroke());
// ui.add_space(6.0);
// Show storage settings.
// self.storage_settings.ui(ui, cb);
self.storage_settings.ui(ui, cb);
}
}
+5 -9
View File
@@ -12,9 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use egui::{Align, Layout, RichText, ScrollArea, StrokeKind};
use egui::scroll_area::ScrollBarVisibility;
use crate::gui::icons::{CHECK, CHECK_FAT, GLOBE_SIMPLE, PENCIL};
use egui::{Align, Layout, RichText, ScrollArea, StrokeKind};
use crate::gui::icons::{CHECK, CHECK_FAT, PENCIL, TRANSLATE};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::types::{ContentContainer, ModalPosition};
use crate::gui::views::{Modal, View};
@@ -64,14 +65,12 @@ impl ContentContainer for InterfaceSettingsContent {
View::radio_value(ui, &mut selected_use_dark, true, t!("dark"));
})
});
ui.add_space(8.0);
ui.add_space(14.0);
if saved_use_dark != selected_use_dark {
AppConfig::set_dark_theme(selected_use_dark);
crate::setup_visuals(ui.ctx());
}
ui.add_space(6.0);
// Draw language selection.
self.language_item_ui(self.locale.clone().as_str(), ui, true, 0, 1);
ui.add_space(4.0);
@@ -155,17 +154,14 @@ impl InterfaceSettingsContent {
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
ui.add_space(12.0);
ui.vertical(|ui| {
// Draw connections URL.
ui.add_space(4.0);
View::ellipsize_text(ui,
t!("lang_name", locale = locale),
18.0,
Colors::title(false));
ui.add_space(1.0);
// Setup connection status text.
let value = format!("{} {}",
GLOBE_SIMPLE,
TRANSLATE,
t!("language"));
ui.label(RichText::new(value).size(15.0).color(Colors::gray()));
ui.add_space(3.0);
+243 -10
View File
@@ -12,38 +12,271 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::gui::icons::COMPUTER_TOWER;
use egui::{Align, Id, Layout, RichText, StrokeKind};
use url::Url;
use crate::gui::icons::{CLOUD_CHECK, CLOUD_SLASH, PENCIL};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::types::ContentContainer;
use crate::gui::views::{Modal, View};
use crate::gui::views::types::{ContentContainer, ModalPosition};
use crate::gui::views::{Modal, TextEdit, View};
use crate::gui::Colors;
use crate::AppConfig;
/// Network communication settings content.
pub struct NetworkSettingsContent {
/// Proxy URL input value for [`Modal`].
proxy_url_edit: String,
/// Flag to check if entered proxy address was correct.
proxy_url_error: bool,
}
/// Identifier for proxy URL edit [`Modal`].
const PROXY_URL_EDIT_MODAL: &'static str = "settings_proxy_edit_modal";
impl ContentContainer for NetworkSettingsContent {
fn modal_ids(&self) -> Vec<&'static str> {
vec![
PROXY_URL_EDIT_MODAL
]
}
fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
match modal.id {
PROXY_URL_EDIT_MODAL => self.proxy_modal_ui(ui, cb),
_ => {}
}
}
fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
View::sub_title(ui, format!("{} {}", COMPUTER_TOWER, t!("network.self")));
View::horizontal_line(ui, Colors::stroke());
ui.add_space(6.0);
fn container_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) {
let use_proxy = AppConfig::use_proxy();
View::checkbox(ui, use_proxy, t!("app_settings.proxy"), || {
// Show edit modal when both URLs are empty.
if AppConfig::http_proxy_url().is_none() && AppConfig::socks_proxy_url().is_none() &&
!use_proxy {
Modal::new(PROXY_URL_EDIT_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("app_settings.proxy"))
.show();
} else {
AppConfig::toggle_use_proxy();
}
});
if !use_proxy {
ui.add_space(4.0);
ui.label(RichText::new(t!("app_settings.proxy_desc"))
.size(16.0)
.color(Colors::inactive_text())
);
ui.add_space(8.0);
} else {
ui.add_space(8.0);
// Draw proxy type selection.
Self::proxy_type_ui(ui);
// Draw proxy URL info.
self.proxy_item_ui(ui);
ui.add_space(6.0);
}
}
}
impl Default for NetworkSettingsContent {
fn default() -> Self {
Self {
proxy_url_edit: "".to_string(),
proxy_url_error: false,
}
}
}
impl NetworkSettingsContent {
/// Draw proxy edit modal content.
fn proxy_modal_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
let on_save = |c: &mut NetworkSettingsContent| {
// Check if URL proxy is correct.
let http = "http://";
let socks = "socks5://";
let proxy = c.proxy_url_edit.trim().to_string();
let use_socks = AppConfig::use_socks_proxy();
// Clear value if empty.
if proxy.is_empty() {
if use_socks {
AppConfig::save_socks_proxy_url(None);
} else {
AppConfig::save_http_proxy_url(None);
}
Modal::close();
return;
}
let url = if use_socks {
let p = proxy.replace(http, "");
if !p.contains(socks) {
format!("{}{}", socks, p)
} else {
p
}
} else {
let p = proxy.replace(socks, "");
if !p.contains(http) {
format!("{}{}", http, p)
} else {
p
}
};
c.proxy_url_error = Url::parse(url.as_str()).is_err();
if !c.proxy_url_error {
// Save result when no error.
if !AppConfig::use_proxy() {
AppConfig::toggle_use_proxy();
}
if use_socks {
AppConfig::save_socks_proxy_url(Some(url))
} else {
AppConfig::save_http_proxy_url(Some(url));
}
Modal::close();
}
};
ui.add_space(6.0);
ui.vertical_centered(|ui| {
let label = format!("{}:", t!("enter_url"));
ui.label(RichText::new(label).size(17.0).color(Colors::gray()));
ui.add_space(8.0);
// Draw proxy URL text edit.
let mut edit = TextEdit::new(
Id::from("proxy_url_edit")
.with(PROXY_URL_EDIT_MODAL)
.with(if AppConfig::use_proxy() {
"socks5"
} else {
"http"
})
).paste();
edit.ui(ui, &mut self.proxy_url_edit, cb);
if edit.enter_pressed {
on_save(self);
}
// Show error when specified address is incorrect.
if self.proxy_url_error {
ui.add_space(10.0);
ui.label(RichText::new(t!("wallets.invalid_url"))
.size(16.0)
.color(Colors::red()));
}
ui.add_space(12.0);
// Show type selection when both URLs are empty.
if AppConfig::socks_proxy_url().is_none() && AppConfig::http_proxy_url().is_none() {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
Self::proxy_type_ui(ui);
});
ui.add_space(4.0);
}
// Show modal buttons.
ui.scope(|ui| {
// Setup spacing between buttons.
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
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!("modal.save"), Colors::white_or_black(false), || {
on_save(self);
});
});
});
ui.add_space(6.0);
});
});
}
/// Draw language selection item content.
fn proxy_item_ui(&mut self, ui: &mut egui::Ui) {
// Setup layout size.
let mut rect = ui.available_rect_before_wrap();
rect.set_height(56.0);
// Draw round background.
let bg_rect = rect.clone();
let item_rounding = View::item_rounding(0, 1, false);
ui.painter().rect(bg_rect,
item_rounding,
Colors::fill(),
View::item_stroke(),
StrokeKind::Middle);
ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| {
View::item_button(ui, View::item_rounding(0, 1, true), PENCIL, None, || {
let url = if AppConfig::use_socks_proxy() {
AppConfig::socks_proxy_url().unwrap_or("".to_string())
} else {
AppConfig::http_proxy_url().unwrap_or("".to_string())
};
self.proxy_url_edit = url;
// Show proxy URL edit modal.
Modal::new(PROXY_URL_EDIT_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("app_settings.proxy"))
.show();
});
let layout_size = ui.available_size();
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
ui.add_space(12.0);
ui.vertical(|ui| {
ui.add_space(4.0);
let use_socks = AppConfig::use_socks_proxy();
let proxy_url = if use_socks {
AppConfig::socks_proxy_url()
} else {
AppConfig::http_proxy_url()
};
let (url, color, icon, text) = if let Some(url) = proxy_url {
(url, Colors::title(false), CLOUD_CHECK, t!("network_settings.enabled"))
} else {
(
t!("enter_url"),
Colors::inactive_text(),
CLOUD_SLASH,
t!("network_settings.disabled")
)
};
View::ellipsize_text(ui, url, 18.0, color);
ui.add_space(1.0);
let value = format!("{} {}", icon, text);
ui.label(RichText::new(value).size(15.0).color(Colors::gray()));
ui.add_space(3.0);
});
});
});
}
/// Draw proxy type selection.
fn proxy_type_ui(ui: &mut egui::Ui) {
// Draw proxy type selection.
let saved_use_socks = AppConfig::use_socks_proxy();
let mut selected_use_socks = saved_use_socks;
ui.columns(2, |columns| {
columns[0].vertical_centered(|ui| {
View::radio_value(ui, &mut selected_use_socks, true, "SOCKS5".to_string());
});
columns[1].vertical_centered(|ui| {
View::radio_value(ui, &mut selected_use_socks, false, "HTTP".to_string());
})
});
ui.add_space(14.0);
if saved_use_socks != selected_use_socks {
AppConfig::toggle_use_socks_proxy();
}
}
}
+1 -3
View File
@@ -34,9 +34,7 @@ impl ContentContainer for StorageSettingsContent {
}
fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
View::sub_title(ui, format!("{} {}", DATABASE, t!("network_node.data")));
View::horizontal_line(ui, Colors::stroke());
ui.add_space(6.0);
}
}
+68
View File
@@ -52,6 +52,15 @@ pub struct AppConfig {
/// Flag to check if dark theme should be used, use system settings if not set.
use_dark_theme: Option<bool>,
/// Flag to use proxy for network requests.
use_proxy: Option<bool>,
/// Flag to use SOCKS5 or HTTP proxy for network requests.
use_socks_proxy: Option<bool>,
/// HTTP proxy URL.
http_proxy_url: Option<String>,
/// SOCKS5 proxy URL.
socks_proxy_url: Option<String>,
}
impl Default for AppConfig {
@@ -69,6 +78,10 @@ impl Default for AppConfig {
lang: None,
english_keyboard: None,
use_dark_theme: None,
use_proxy: None,
use_socks_proxy: None,
http_proxy_url: None,
socks_proxy_url: None,
}
}
}
@@ -261,4 +274,59 @@ impl AppConfig {
w_config.use_dark_theme = Some(use_dark);
w_config.save();
}
/// Check if proxy for network requests is needed.
pub fn use_proxy() -> bool {
let r_config = Settings::app_config_to_read();
r_config.use_proxy.clone().unwrap_or(false)
}
/// Enable or disable proxy for network requests.
pub fn toggle_use_proxy() {
let use_proxy = Self::use_proxy();
let mut w_config = Settings::app_config_to_update();
w_config.use_proxy = Some(!use_proxy);
w_config.save();
}
/// Check if SOCKS5 or HTTP proxy should be used.
pub fn use_socks_proxy() -> bool {
let r_config = Settings::app_config_to_read();
r_config.use_socks_proxy.clone().unwrap_or(true)
}
/// Enable SOCKS5 or HTTP proxy.
pub fn toggle_use_socks_proxy() {
let use_proxy = Self::use_socks_proxy();
let mut w_config = Settings::app_config_to_update();
w_config.use_socks_proxy = Some(!use_proxy);
w_config.save();
}
/// Get SOCKS proxy URL.
pub fn socks_proxy_url() -> Option<String> {
let r_config = Settings::app_config_to_read();
r_config.socks_proxy_url.clone()
}
/// Save SOCKS proxy URL.
pub fn save_socks_proxy_url(url: Option<String>) {
let mut w_config = Settings::app_config_to_update();
w_config.socks_proxy_url = url;
w_config.save();
}
/// Get HTTP proxy URL.
pub fn http_proxy_url() -> Option<String> {
let r_config = Settings::app_config_to_read();
r_config.http_proxy_url.clone()
}
/// Save HTTP proxy URL.
pub fn save_http_proxy_url(url: Option<String>) {
let mut w_config = Settings::app_config_to_update();
w_config.http_proxy_url = url;
w_config.save();
}
}