From e436be400e12720f74e2943d145672c072a70e3c Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Sat, 18 Jan 2025 12:53:30 -0800 Subject: [PATCH] add add relay GUI --- assets/icons/add_relay_icon_4x.png | Bin 0 -> 3081 bytes crates/enostr/src/relay/pool.rs | 18 +++ crates/notedeck_columns/src/nav.rs | 2 +- .../src/relay_pool_manager.rs | 5 + crates/notedeck_columns/src/ui/add_column.rs | 2 +- crates/notedeck_columns/src/ui/relay.rs | 112 +++++++++++++++--- 6 files changed, 121 insertions(+), 18 deletions(-) create mode 100644 assets/icons/add_relay_icon_4x.png diff --git a/assets/icons/add_relay_icon_4x.png b/assets/icons/add_relay_icon_4x.png new file mode 100644 index 0000000000000000000000000000000000000000..1f11a71c3115c4b781068c7e0829f9789920a81e GIT binary patch literal 3081 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 bool { + if url.is_empty() { + return false; + } + let url = match Url::parse(url) { + Ok(parsed_url) => parsed_url.to_string(), + Err(_err) => { + // debug!("bad relay url \"{}\": {:?}", url, err); + return false; + } + }; + if self.has(&url) { + return false; + } + true + } + // Adds a websocket url to the RelayPool. pub fn add_url( &mut self, diff --git a/crates/notedeck_columns/src/nav.rs b/crates/notedeck_columns/src/nav.rs index 4a68129..9a45e84 100644 --- a/crates/notedeck_columns/src/nav.rs +++ b/crates/notedeck_columns/src/nav.rs @@ -291,7 +291,7 @@ fn render_nav_body( } Route::Relays => { let manager = RelayPoolManager::new(ctx.pool); - RelayView::new(manager).ui(ui); + RelayView::new(manager, &mut app.view_state.id_string_map).ui(ui); None } Route::ComposeNote => { diff --git a/crates/notedeck_columns/src/relay_pool_manager.rs b/crates/notedeck_columns/src/relay_pool_manager.rs index 1e3612a..1a92a20 100644 --- a/crates/notedeck_columns/src/relay_pool_manager.rs +++ b/crates/notedeck_columns/src/relay_pool_manager.rs @@ -44,6 +44,11 @@ impl<'a> RelayPoolManager<'a> { pub fn add_relay(&mut self, ctx: &egui::Context, relay_url: String) { let _ = self.pool.add_url(relay_url, create_wakeup(ctx)); } + + /// check whether a relay url is valid + pub fn is_valid_relay(&self, url: &str) -> bool { + self.pool.is_valid_url(url) + } } pub fn create_wakeup(ctx: &egui::Context) -> impl Fn() + Send + Sync + Clone + 'static { diff --git a/crates/notedeck_columns/src/ui/add_column.rs b/crates/notedeck_columns/src/ui/add_column.rs index 6a38000..eb80398 100644 --- a/crates/notedeck_columns/src/ui/add_column.rs +++ b/crates/notedeck_columns/src/ui/add_column.rs @@ -447,7 +447,7 @@ fn add_column_button() -> impl Widget { sized_button("Add") } -fn sized_button(text: &str) -> impl Widget + '_ { +pub(crate) fn sized_button(text: &str) -> impl Widget + '_ { move |ui: &mut egui::Ui| -> egui::Response { let painter = ui.painter(); let galley = painter.layout( diff --git a/crates/notedeck_columns/src/ui/relay.rs b/crates/notedeck_columns/src/ui/relay.rs index 273b4e2..beff003 100644 --- a/crates/notedeck_columns/src/ui/relay.rs +++ b/crates/notedeck_columns/src/ui/relay.rs @@ -1,12 +1,21 @@ +use std::collections::HashMap; + +use crate::colors::PINK; use crate::relay_pool_manager::{RelayPoolManager, RelayStatus}; use crate::ui::{Preview, PreviewConfig, View}; -use egui::{Align, Button, Frame, Layout, Margin, Rgba, RichText, Rounding, Ui, Vec2}; +use egui::{Align, Button, Frame, Id, Image, Layout, Margin, Rgba, RichText, Rounding, Ui, Vec2}; use enostr::RelayPool; use notedeck::NotedeckTextStyle; +use tracing::debug; + +use super::add_column::sized_button; +use super::padding; + pub struct RelayView<'a> { manager: RelayPoolManager<'a>, + id_string_map: &'a mut HashMap, } impl View for RelayView<'_> { @@ -19,13 +28,6 @@ impl View for RelayView<'_> { RichText::new("Relays").text_style(NotedeckTextStyle::Heading2.text_style()), ); }); - - // TODO: implement manually adding relays - // ui.with_layout(Layout::right_to_left(Align::Center), |ui| { - // if ui.add(add_relay_button()).clicked() { - // // TODO: navigate to 'add relay view' - // }; - // }); }); ui.add_space(8.0); @@ -37,13 +39,20 @@ impl View for RelayView<'_> { if let Some(indices) = self.show_relays(ui) { self.manager.remove_relays(indices); } + ui.add_space(8.0); + if let Some(add_relay) = self.show_add_relay_ui(ui) { + debug!("add relay \"{}\"", add_relay); + } }); } } impl<'a> RelayView<'a> { - pub fn new(manager: RelayPoolManager<'a>) -> Self { - RelayView { manager } + pub fn new(manager: RelayPoolManager<'a>, id_string_map: &'a mut HashMap) -> Self { + RelayView { + manager, + id_string_map, + } } pub fn panel(&mut self, ui: &mut egui::Ui) { @@ -102,6 +111,81 @@ impl<'a> RelayView<'a> { indices_to_remove } + + const RELAY_PREFILL: &'static str = "wss://"; + + fn show_add_relay_ui(&mut self, ui: &mut Ui) -> Option { + let id = ui.id().with("add-relay)"); + match self.id_string_map.get(&id) { + None => { + ui.with_layout(Layout::top_down(Align::Min), |ui| { + let relay_button = add_relay_button(); + if ui.add(relay_button).clicked() { + debug!("add relay clicked"); + self.id_string_map + .insert(id, Self::RELAY_PREFILL.to_string()); + }; + }); + None + } + Some(_) => { + ui.with_layout(Layout::top_down(Align::Min), |ui| { + self.add_relay_entry(ui, id) + }) + .inner + } + } + } + + pub fn add_relay_entry(&mut self, ui: &mut Ui, id: Id) -> Option { + padding(16.0, ui, |ui| { + let text_buffer = self + .id_string_map + .entry(id) + .or_insert_with(|| Self::RELAY_PREFILL.to_string()); + let is_enabled = self.manager.is_valid_relay(text_buffer); + let text_edit = egui::TextEdit::singleline(text_buffer) + .hint_text( + RichText::new("Enter the relay here") + .text_style(NotedeckTextStyle::Body.text_style()), + ) + .vertical_align(Align::Center) + .desired_width(f32::INFINITY) + .min_size(Vec2::new(0.0, 40.0)) + .margin(Margin::same(12.0)); + ui.add(text_edit); + ui.add_space(8.0); + if ui + .add_sized(egui::vec2(50.0, 40.0), add_relay_button2(is_enabled)) + .clicked() + { + self.id_string_map.remove(&id) // remove and return the value + } else { + None + } + }) + .inner + } +} + +fn add_relay_button() -> Button<'static> { + let img_data = egui::include_image!("../../../../assets/icons/add_relay_icon_4x.png"); + let img = Image::new(img_data).fit_to_exact_size(Vec2::new(48.0, 48.0)); + Button::image_and_text( + img, + RichText::new(" Add relay") + .size(16.0) + // TODO: this color should not be hard coded. Find some way to add it to the visuals + .color(PINK), + ) + .frame(false) +} + +fn add_relay_button2(is_enabled: bool) -> impl egui::Widget + 'static { + move |ui: &mut egui::Ui| -> egui::Response { + let button_widget = sized_button("Add"); + ui.add_enabled(is_enabled, button_widget) + } } fn get_right_side_width(status: RelayStatus) -> f32 { @@ -112,11 +196,6 @@ fn get_right_side_width(status: RelayStatus) -> f32 { } } -#[allow(unused)] -fn add_relay_button() -> egui::Button<'static> { - Button::new("+ Add relay").min_size(Vec2::new(0.0, 32.0)) -} - fn delete_button(_dark_mode: bool) -> egui::Button<'static> { /* let img_data = if dark_mode { @@ -201,7 +280,8 @@ mod preview { impl App for RelayViewPreview { fn update(&mut self, _app: &mut AppContext<'_>, ui: &mut egui::Ui) { self.pool.try_recv(); - RelayView::new(RelayPoolManager::new(&mut self.pool)).ui(ui); + let mut id_string_map = HashMap::new(); + RelayView::new(RelayPoolManager::new(&mut self.pool), &mut id_string_map).ui(ui); } }