revamp settings page, relay management
This commit is contained in:
parent
e4d8aa62ee
commit
002c001501
8 changed files with 194 additions and 41 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
23
src/main.rs
23
src/main.rs
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
134
src/ui/settings.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue