Skip to content

Commit

Permalink
Add IosevkaAile fonts, update chars.txt and assets, refactor dashboar…
Browse files Browse the repository at this point in the history
…d UI and logging, adjust main window size and fonts setup, improve client error handling
  • Loading branch information
nullchinchilla committed Apr 5, 2024
1 parent 99834f5 commit 460487e
Show file tree
Hide file tree
Showing 11 changed files with 96 additions and 91 deletions.
Binary file not shown.
Binary file not shown.
20 changes: 18 additions & 2 deletions binaries/geph5-client-gui/src/assets/chars.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
>
?
[
\
]
_
{
Expand Down Expand Up @@ -76,6 +77,7 @@ x
y
z
а
б
в
В
г
Expand All @@ -84,6 +86,7 @@ z
е
Ж
з
З
и
И
й
Expand All @@ -103,6 +106,7 @@ z
у
ф
х
ц
ч
ы
ь
Expand All @@ -114,6 +118,7 @@ z
پ
ت
ج
ح
خ
د
ذ
Expand All @@ -139,35 +144,45 @@ z
ی
Expand All @@ -177,6 +192,7 @@ z
退
Expand Down
Binary file not shown.
Binary file not shown.
3 changes: 2 additions & 1 deletion binaries/geph5-client-gui/src/assets/subset.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/bin/bash
find ../ -type f -name '*.rs' -o -name '*.csv' | xargs cat | grep -o . | sort -u > chars.txt
pyftsubset SarasaUiSC-Regular.ttf --text-file=chars.txt --output-file=subset.ttf
pyftsubset IosevkaAile-Regular.ttf --text-file=chars.txt --output-file=normal.ttf
pyftsubset SarasaUiSC-Regular.ttf --text-file=chars.txt --output-file=chinese.ttf
4 changes: 4 additions & 0 deletions binaries/geph5-client-gui/src/daemon.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
use egui::mutex::Mutex;
use once_cell::sync::Lazy;

pub static DAEMON: Lazy<Mutex<Option<geph5_client::Client>>> = Lazy::new(|| Mutex::new(None));
99 changes: 30 additions & 69 deletions binaries/geph5-client-gui/src/dashboard.rs
Original file line number Diff line number Diff line change
@@ -1,81 +1,42 @@
use std::time::Duration;
use crate::{daemon::DAEMON, l10n::l10n, settings::get_config};

use anyhow::Context as _;
use futures_util::{future::Shared, FutureExt};
use geph5_broker_protocol::{BrokerClient, ExitList};

use smol::Task;
use smol_timeout::TimeoutExt as _;

use crate::{l10n::l10n, settings::get_config};

pub struct Dashboard {
selected_server: Option<(String, String)>,
server_list: Shared<Task<ExitList>>,
}
pub struct Dashboard {}

impl Dashboard {
pub fn new() -> Self {
Self {
selected_server: None,
server_list: smolscale::spawn(get_server_list()).shared(),
}
Self {}
}
pub fn render(&mut self, ui: &mut egui::Ui) -> anyhow::Result<()> {
ui.label(l10n("selected_server"));
egui::ComboBox::from_label("")
.selected_text(render_exit_selection(&self.selected_server))
.show_ui(ui, |ui| {
if let Some(list) = self.server_list.peek() {
} else {
ui.label(l10n("loading_exit_list"));
}
// ui.selectable_value(&mut self.selected_server, "Apple".to_string(), "Apple");
// ui.selectable_value(&mut self.selected_server, "Pear".to_string(), "Pear");
// ui.selectable_value(&mut self.selected_server, "Orange".to_string(), "Orange");
});
anyhow::Ok(())
}
}
let mut daemon = DAEMON.lock();

fn render_exit_selection(selection: &Option<(String, String)>) -> String {
if let Some((country, city)) = selection.as_ref() {
format!("{country}/{city}")
} else {
"Auto".into()
}
}
ui.columns(2, |columns| {
columns[0].label(l10n("status"));
if daemon.is_none() {
columns[1].colored_label(egui::Color32::DARK_RED, l10n("disconnected"));
}
columns[0].label(l10n("connection_time"));
columns[0].label(l10n("data_used"));
});
ui.add_space(10.);
ui.vertical_centered(|ui| {
if daemon.is_none() {
if ui.button(l10n("connect")).clicked() {
tracing::warn!("connect clicked");
*daemon = Some(geph5_client::Client::start(get_config()?));
}
} else if ui.button(l10n("disconnect")).clicked() {
tracing::warn!("disconnect clicked");
*daemon = None;
}
anyhow::Ok(())
})
.inner?;

async fn get_server_list() -> ExitList {
loop {
let fallible = async {
let broker_client = get_broker_client()
.await?
.context("no broker client available")?;
let exits = broker_client
.get_exits()
.timeout(Duration::from_secs(10))
.await
.context("timeout")??
.map_err(|e| anyhow::anyhow!(e))?
.inner;
anyhow::Ok(exits)
};
match fallible.await {
Ok(res) => return res,
Err(err) => {
tracing::warn!(err = debug(err), "error getting server list");
smol::Timer::after(Duration::from_secs(1)).await;
if let Some(daemon) = daemon.as_ref() {
if let Err(err) = daemon.check_dead() {
ui.colored_label(egui::Color32::RED, format!("{:?}", err));
}
}
}
}

async fn get_broker_client() -> anyhow::Result<Option<BrokerClient>> {
let config = get_config()?;
if let Some(src) = config.broker {
Ok(Some(BrokerClient::from(src.rpc_transport())))
} else {
Ok(None)
Ok(())
}
}
6 changes: 3 additions & 3 deletions binaries/geph5-client-gui/src/l10n.csv
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ account_info,Account Info,帐户信息,Информация об аккаунт
cancel,Cancel,取消,Отмена,لغو
connect,Connect,连接,Подключиться,اتصال
connected,Connected,已连接,Подключено,متصل
connection_time,Connection Time,连接时间,Время подключения,زمان اتصال
connection_time,Connection time,连接时间,Время подключения,زمان اتصال
dashboard,Dashboard,仪表盘,Панель,داشبورد
data_used,Data Used,已用流量,Использовано данных,داده مصرف شده
data_used,Data used,已用流量,Использовано данных,داده مصرف شده
disconnect,Disconnect,断开连接,Отключиться,قطع اتصال
disconnected,Disconnected,已断开连接,Отключено,قطع اتصال
download_speed,Download Speed,下载速度,Скорость скачивания,سرعت دانلود
download_speed,Download speed,下载速度,Скорость скачивания,سرعت دانلود
exit,Exit,退出,Выход,خروج
geph,Geph,迷雾通,Геф,گف
help,Help,帮助,Справка,راهنما
Expand Down
23 changes: 13 additions & 10 deletions binaries/geph5-client-gui/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod daemon;
mod dashboard;
mod l10n;
mod prefs;
Expand All @@ -23,7 +24,7 @@ fn main() {
)
.with(
EnvFilter::builder()
.with_default_directive("geph5=debug".parse().unwrap())
.with_default_directive("geph5".parse().unwrap())
.from_env_lossy(),
)
.init();
Expand All @@ -36,8 +37,9 @@ fn main() {

let native_options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_inner_size([400.0, 300.0])
.with_min_inner_size([400.0, 300.0]),
.with_inner_size([300.0, 300.0])
.with_min_inner_size([300.0, 300.0])
.with_max_inner_size([300.0, 300.0]),

..Default::default()
};
Expand Down Expand Up @@ -67,23 +69,24 @@ impl App {
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
// light mode
cc.egui_ctx.set_visuals(
Visuals::light()
.tap_mut(|vis| vis.widgets.noninteractive.fg_stroke.color = Color32::BLACK),
Visuals::light(), // .tap_mut(|vis| vis.widgets.noninteractive.fg_stroke.color = Color32::BLACK),
);

cc.egui_ctx.set_zoom_factor(1.1);

// set up fonts. currently this uses SC for CJK, but this can be autodetected instead.
let mut fonts = FontDefinitions::default();
fonts.font_data.insert(
"sarasa_sc".into(),
FontData::from_static(include_bytes!("assets/subset.ttf")),
"normal".into(),
FontData::from_static(include_bytes!("assets/normal.ttf")),
);
fonts.font_data.insert(
"chinese".into(),
FontData::from_static(include_bytes!("assets/chinese.ttf")),
);
fonts
.families
.get_mut(&FontFamily::Proportional)
.unwrap()
.insert(0, "sarasa_sc".into());
.insert(0, "chinese".into());

cc.egui_ctx.set_fonts(fonts);
Self {
Expand Down
32 changes: 26 additions & 6 deletions binaries/geph5-client/src/client.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use anyctx::AnyCtx;
use clone_macro::clone;
use futures_util::TryFutureExt;
use futures_util::{
future::{FusedFuture, Shared},
task::noop_waker,
FutureExt, TryFutureExt,
};
use geph5_broker_protocol::{Credential, ExitList};
use smol::future::FutureExt as _;
use std::{net::SocketAddr, path::PathBuf, time::Duration};
use std::{net::SocketAddr, path::PathBuf, sync::Arc, task::Context, time::Duration};

use serde::{Deserialize, Serialize};
use smolscale::immortal::{Immortal, RespawnStrategy};
Expand All @@ -30,20 +34,36 @@ pub struct Config {
}

pub struct Client {
task: smol::Task<anyhow::Result<()>>,
task: Shared<smol::Task<Result<(), Arc<anyhow::Error>>>>,
}

impl Client {
/// Starts the client logic in the loop, returnign the handle.
pub fn start(cfg: Config) -> Self {
let ctx = AnyCtx::new(cfg);
let task = smolscale::spawn(client_main(ctx));
Client { task }
let task = smolscale::spawn(client_main(ctx).map_err(Arc::new));
Client {
task: task.shared(),
}
}

/// Wait until there's an error.
pub async fn wait_until_dead(self) -> anyhow::Result<()> {
self.task.await
self.task.await.map_err(|e| anyhow::anyhow!(e))
}

/// Check for an error.
pub fn check_dead(&self) -> anyhow::Result<()> {
match self
.task
.clone()
.poll(&mut Context::from_waker(&noop_waker()))
{
std::task::Poll::Ready(val) => val.map_err(|e| anyhow::anyhow!(e))?,
std::task::Poll::Pending => {}
}

Ok(())
}
}

Expand Down

0 comments on commit 460487e

Please sign in to comment.