revamp settings page, relay management

This commit is contained in:
Jack Chakany 2024-08-23 17:43:07 -04:00
parent e4d8aa62ee
commit 002c001501
8 changed files with 194 additions and 41 deletions

10
Cargo.lock generated
View file

@ -1406,6 +1406,15 @@ dependencies = [
"winit",
]
[[package]]
name = "egui_tabs"
version = "0.1.0"
source = "git+https://github.com/damus-io/egui-tabs?rev=120971fc43db6ba0b6f194f4bd4a66f7e00a4e22#120971fc43db6ba0b6f194f4bd4a66f7e00a4e22"
dependencies = [
"egui",
"egui_extras",
]
[[package]]
name = "either"
version = "1.11.0"
@ -2073,6 +2082,7 @@ version = "0.1.0"
dependencies = [
"eframe",
"egui_extras",
"egui_tabs",
"ewebsock",
"image 0.25.1",
"nostr",

View file

@ -15,6 +15,7 @@ egui_extras = { version = "0.27.2", features = [
"image",
"svg",
] }
egui_tabs = { git = "https://github.com/damus-io/egui-tabs", rev = "120971fc43db6ba0b6f194f4bd4a66f7e00a4e22" }
image = { version = "0.25.1", features = ["jpeg", "png"] }
tracing = "0.1.40"
tracing-appender = "0.2.3"

View file

@ -68,6 +68,7 @@ pub enum Page {
#[derive(Default)]
pub struct HootState {
pub onboarding: ui::onboarding::OnboardingState,
pub settings: ui::settings::SettingsState,
}
pub struct Hoot {
@ -99,7 +100,7 @@ fn update_app(app: &mut Hoot, ctx: &egui::Context) {
ctx.request_repaint();
};
match app.account_manager.load_keys() {
Ok(..) => {},
Ok(..) => {}
Err(v) => error!("something went wrong trying to load keys: {}", v),
}
app.relays
@ -278,25 +279,7 @@ fn render_app(app: &mut Hoot, ctx: &egui::Context) {
});
} else if app.page == Page::Settings {
ui.heading("Settings");
ui.label(format!(
"Connected Relays: {}",
&app.relays.get_number_of_relays()
));
ui.vertical(|ui| {
use nostr::ToBech32;
for key in app.account_manager.loaded_keys.clone() {
ui.horizontal(|ui| {
ui.label(format!("Key ID: {}", key.public_key().to_bech32().unwrap()));
if ui.button("Remove Key").clicked() {
match app.account_manager.delete_key(&key) {
Ok(..) => {},
Err(v) => error!("couldn't remove key: {}", v),
}
}
});
}
});
ui::settings::SettingsScreen::ui(app, ui);
}
});
}

View file

@ -12,7 +12,7 @@ pub use message::{ClientMessage, RelayMessage};
mod subscription;
pub use subscription::Subscription;
#[derive(PartialEq)]
#[derive(PartialEq, Clone, Copy)]
pub enum RelayStatus {
Connecting,
Connected,
@ -20,10 +20,10 @@ pub enum RelayStatus {
}
pub struct Relay {
url: String,
pub url: String,
reader: ewebsock::WsReceiver,
writer: ewebsock::WsSender,
status: RelayStatus,
pub status: RelayStatus,
}
impl Relay {
@ -36,12 +36,16 @@ impl Relay {
ewebsock::connect_with_wakeup(new_url.clone(), ewebsock::Options::default(), wake_up)
.unwrap();
Self {
let mut relay = Self {
url: new_url,
reader: reciever,
writer: sender,
status: RelayStatus::Connected,
}
status: RelayStatus::Connecting,
};
relay.ping();
relay
}
pub fn send(&mut self, message: WsMessage) -> Result<()> {
@ -99,4 +103,18 @@ impl Relay {
None
}
pub fn ping(&mut self) {
let ping_msg = WsMessage::Ping(Vec::new());
match self.send(ping_msg) {
Ok(_) => {
info!("Ping sent to {}", self.url);
self.status = RelayStatus::Connected;
}
Err(e) => {
error!("Error sending ping to {}: {:?}", self.url, e);
self.status = RelayStatus::Disconnected;
}
}
}
}

View file

@ -3,7 +3,7 @@ use crate::relay::{Relay, RelayStatus};
use std::collections::HashMap;
pub struct RelayPool {
relays: HashMap<String, Relay>,
pub relays: HashMap<String, Relay>,
}
impl RelayPool {
@ -15,31 +15,35 @@ impl RelayPool {
pub fn add_url(&mut self, url: String, wake_up: impl Fn() + Send + Sync + 'static) {
let relay = Relay::new_with_wakeup(url.clone(), wake_up);
self.relays.insert(url, relay);
}
pub fn remove_url(&mut self, url: &str) -> Option<Relay> {
self.relays.remove(url)
}
pub fn try_recv(&mut self) -> Option<String> {
for relay in &mut self.relays {
if let Some(message) = relay.1.try_recv() {
for relay in self.relays.values_mut() {
if let Some(message) = relay.try_recv() {
return Some(message);
}
}
return None;
None
}
pub fn send(&mut self, message: ewebsock::WsMessage) -> Result<()> {
for relay in &mut self.relays {
if relay.1.status == RelayStatus::Connected {
relay.1.send(message.clone())?;
for relay in self.relays.values_mut() {
if relay.status == RelayStatus::Connected {
relay.send(message.clone())?;
}
}
Ok(())
}
pub fn get_number_of_relays(&mut self) -> usize {
self.relays.len()
pub fn ping_all(&mut self) -> Result<()> {
for relay in self.relays.values_mut() {
relay.ping();
}
Ok(())
}
}

View file

@ -2,6 +2,7 @@ use eframe::egui;
pub mod compose_window;
pub mod onboarding;
pub mod settings;
pub trait View {
fn ui(&mut self, ui: &mut egui::Ui);

View file

@ -19,7 +19,7 @@ impl OnboardingScreen {
Page::OnboardingNew => Self::onboarding_new(app, ui),
Page::OnboardingNewShowKey => Self::onboarding_new_keypair_generated(app, ui),
Page::OnboardingReturning => Self::onboarding_returning(app, ui),
_ => error!("OnboardingScreen should not be displayed when page is not Onboarding!"),
_ => error!("OnboardingScreen should not be displayed when page is not Onboarding!"),
}
}
@ -70,7 +70,6 @@ _ => error!("OnboardingScreen should not be displayed when page is not Onboardin
}
ui.label("Welcome Back!");
let parsed_secret_key = nostr::SecretKey::parse(&app.state.onboarding.secret_input);
let valid_key = parsed_secret_key.is_ok();
ui.horizontal(|ui| {
@ -82,7 +81,10 @@ _ => error!("OnboardingScreen should not be displayed when page is not Onboardin
}
});
if ui.add_enabled(valid_key, egui::Button::new("Save")).clicked() {
if ui
.add_enabled(valid_key, egui::Button::new("Save"))
.clicked()
{
use crate::keystorage::KeyStorage;
let keypair = nostr::Keys::new(parsed_secret_key.unwrap());
let _ = app.account_manager.add_key(&keypair);

134
src/ui/settings.rs Normal file
View file

@ -0,0 +1,134 @@
use crate::Hoot;
use eframe::egui::{self, Color32, Direction, Layout, Sense, Ui, Vec2};
use egui_tabs::Tabs;
use tracing::error;
#[derive(Default)]
pub struct SettingsState {
pub new_relay_url: String,
}
enum Tab {
Profile = 0,
Relays = 1,
Identity = 2,
}
impl From<i32> for Tab {
fn from(value: i32) -> Self {
match value {
0 => Tab::Profile,
1 => Tab::Relays,
2 => Tab::Identity,
_ => Tab::Profile, // Default to Profile for invalid values
}
}
}
impl From<Tab> for i32 {
fn from(tab: Tab) -> Self {
tab as i32
}
}
pub struct SettingsScreen {}
impl SettingsScreen {
pub fn ui(app: &mut Hoot, ui: &mut Ui) {
let tabs_response = Tabs::new(3)
.height(16.0)
.selected(0)
.layout(Layout::centered_and_justified(Direction::TopDown))
.show(ui, |ui, state| {
let current_tab = Tab::from(state.index());
use Tab::*;
let tab_label = match current_tab {
Profile => "My Profile",
Relays => "Relays",
Identity => "Keys",
};
ui.add(egui::Label::new(tab_label).selectable(false));
});
let current_tab: Tab = tabs_response.selected().unwrap().into();
use Tab::*;
match current_tab {
Profile => Self::profile(app, ui),
Relays => Self::relays(app, ui),
Identity => Self::identity(app, ui),
}
}
fn profile(app: &mut Hoot, ui: &mut Ui) {
ui.label("Your profile.");
}
fn relays(app: &mut Hoot, ui: &mut Ui) {
ui.heading("Relays");
ui.small("A relay is a server that Hoot connects with to send & receive messages.");
ui.label("Add New Relay:");
ui.horizontal(|ui| {
let new_relay = &mut app.state.settings.new_relay_url;
ui.text_edit_singleline(new_relay);
if ui.button("Add Relay").clicked() && !new_relay.is_empty() {
let ctx = ui.ctx().clone();
let wake_up = move || {
ctx.request_repaint();
};
app.relays.add_url(new_relay.clone(), wake_up);
app.state.settings.new_relay_url = String::new(); // clears field
}
});
ui.add_space(10.0);
ui.label("Your Relays:");
ui.vertical(|ui| {
let mut relay_to_remove: Option<String> = None;
for (url, relay) in app.relays.relays.iter() {
ui.horizontal(|ui| {
use crate::relay::RelayStatus::*;
let conn_fill: Color32 = match relay.status {
Connecting => Color32::YELLOW,
Connected => Color32::LIGHT_GREEN,
Disconnected => Color32::RED,
};
let size = Vec2::splat(12.0);
let (response, painter) = ui.allocate_painter(size, Sense::hover());
let rect = response.rect;
let c = rect.center();
let r = rect.width() / 2.0 - 1.0;
painter.circle_filled(c, r, conn_fill);
ui.label(url);
if ui.button("Remove Relay").clicked() {
relay_to_remove = Some(url.to_string());
}
});
}
if relay_to_remove.is_some() {
app.relays.remove_url(&relay_to_remove.unwrap());
}
});
}
fn identity(app: &mut Hoot, ui: &mut Ui) {
ui.vertical(|ui| {
use nostr::ToBech32;
for key in app.account_manager.loaded_keys.clone() {
ui.horizontal(|ui| {
ui.label(format!("Key ID: {}", key.public_key().to_bech32().unwrap()));
if ui.button("Remove Key").clicked() {
match app.account_manager.delete_key(&key) {
Ok(..) => {}
Err(v) => error!("couldn't remove key: {}", v),
}
}
});
}
});
}
}