From 349e3baa995199afe9211d28cbebae27bc4e911a Mon Sep 17 00:00:00 2001 From: kernelkind Date: Wed, 17 Apr 2024 13:36:25 -0400 Subject: [PATCH] Add relay view Signed-off-by: kernelkind Signed-off-by: William Casarin --- assets/icons/connected_icon_4x.png | Bin 0 -> 1872 bytes assets/icons/connecting_icon_4x.png | Bin 0 -> 1733 bytes assets/icons/delete_icon_4x.png | Bin 0 -> 1057 bytes assets/icons/disconnected_icon_4x.png | Bin 0 -> 1863 bytes src/lib.rs | 2 + src/relay_pool_manager.rs | 54 ++++++++ src/relay_view.rs | 171 ++++++++++++++++++++++++++ 7 files changed, 227 insertions(+) create mode 100644 assets/icons/connected_icon_4x.png create mode 100644 assets/icons/connecting_icon_4x.png create mode 100644 assets/icons/delete_icon_4x.png create mode 100644 assets/icons/disconnected_icon_4x.png create mode 100644 src/relay_pool_manager.rs create mode 100644 src/relay_view.rs diff --git a/assets/icons/connected_icon_4x.png b/assets/icons/connected_icon_4x.png new file mode 100644 index 0000000000000000000000000000000000000000..9a84d1daf6dfe9c39dec31396fbe4f83608a9f77 GIT binary patch literal 1872 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 bO%3=3C8)0(6i=U-00000NkvXXu0mjfC4kr( literal 0 HcmV?d00001 diff --git a/assets/icons/disconnected_icon_4x.png b/assets/icons/disconnected_icon_4x.png new file mode 100644 index 0000000000000000000000000000000000000000..7dc34f862ae478da79be209e9c68dbbad40505cd GIT binary patch literal 1863 zcmV-N2e|l&P)@~0drDELIAGL9O(c600d`2O+f$vv5yP@09_Bo4Chf;xUA*0r9HK9b5MnGp}{Urfz`F`mF2zk^yWf3to0iEj8 zI*{Ju#{g!FQiv!_K(xlce%nLTkKkH?O~9KwMRi0W0^Z+iEGc#N4m`ekq|_8EHK356 zKk-sIO;rhG`4FkQq`j49SgF9{B0GmS0ln(RGfmWVsVhROiry!x9M-yT+Tej`X~9JU z2!8`dq6z55TJP;^@PHQqz3Tc)O|YF?5m{pc?)VCAL#MjVM8HdMA%e$OJ8JEKBb*6v z6rL56+AK@(G@Oa>Ai;@%?&{O$1p0M$pRl{I@O68+eRv923?^dxk_amlVq>*?un#%N zCE!xw9oyc^IL@F12UA1toeBnUJ#E3H?0+7Q{4z70nyjDOnu%8cV?~d zT(EZZ=1_Jb%j@9%3hx{Sm}h|f>xcJ`TH7$E7z}Q0gGC=y5T2XLCkV*;`f~b$ImUw% z*7j_Oncng2Sd6#Maom`!iqE3zlL&o|o{4|pviIiN$AcH(fuik2*>$QLZPwKF^t;$m z17pi~YzD;pzQvQ{@%6i_8?U(jwrJ((PtjIqbz{%9eN;Eq-nNF#K+9OEF30%MlGGA7 ztD778eSKDxjmc7KxR4Onp+>sgwuViRSs+`r>0Q4+XpH`!B0#49B-lCGhEkACf=&2B zh@7d0Vo(o9{~sej91*5Q1Sa+wT+=UJ$w92l$152r|*@uW%|=6C5ubar1V15?pq+(OtkY12BMsaIx_6?1X7ZK+DG?t%k&5Zl&E2|oUS%Z`j^ko4JR{5VYTr>PNF$~e{ej49j5 zDQY%bdaE0YQ+|Rm0tSPdP2*2_E(4Q4z}3RDk>KMG$aL(cbln&Mu~!w_#&$%R(b!S2 z`)pf$^rBNDy~6%PAM-XMA>F>$+H!4gck5$29nhHwjm2qh`klGGJ;XFG$O`GPw3}() z=7cF6MOZ9Wp@f*yUe&onMo`D(*fpL7GJJYL|I4}aQkXR)iv>J`I`-)B>iXZdA5)4R z&s%)3S9|hEYkX&U9oBlSJEj-*)1W29Bp{7@Q4nQLAY-|>P0BtYx}t>JM-l>q7N zSk@oR7}6){bBSXFwmLHQyhIMCG`Y;ZHGyY>Vo}I8Jm{ZuO_FRB{vUB zPGz4QgIi|0WU;s=aySzZ55S28L8(NoH=eVb@Q7P!25gaOG<%%x;(-OcX%R~qad}r$Ym-P^e(UquD~6mP!!(% zk26S@PY~Y%LtYYCim#w3Sb)Qy7QSv4%~38)KmxB+_Ht25X8P0wB$yQuah59Wiq8Q` zKxqUdh`lkqEU4sbbL^;5sCUaggq2P}g5;*CkWt_c(D06t_$e0_zBI4u9>SFokcR9W zh$@gojQDaC-X0qW?+jY2Fb>e@_B`bx|G { + pub pool: &'a mut RelayPool, +} + +pub struct RelayInfo<'a> { + pub relay_url: &'a str, + pub status: &'a RelayStatus, +} + +impl<'a> RelayPoolManager<'a> { + pub fn new(pool: &'a mut RelayPool) -> Self { + RelayPoolManager { pool } + } + + pub fn get_relay_infos(&self) -> Vec { + self.pool + .relays + .iter() + .map(|relay| RelayInfo { + relay_url: &relay.relay.url, + status: &relay.relay.status, + }) + .collect() + } + + /// index of the Vec from get_relay_infos + pub fn remove_relay(&mut self, index: usize) { + if index < self.pool.relays.len() { + self.pool.relays.remove(index); + } + } + + /// removes all specified relay indicies shown in get_relay_infos + pub fn remove_relays(&mut self, mut indices: Vec) { + indices.sort_unstable_by(|a, b| b.cmp(a)); + indices.iter().for_each(|index| self.remove_relay(*index)); + } + + pub fn add_relay(&mut self, ctx: &egui::Context, relay_url: String) { + let _ = self.pool.add_url(relay_url, create_wakeup(ctx)); + } +} + +fn create_wakeup(ctx: &egui::Context) -> impl Fn() + Send + Sync + Clone + 'static { + let ctx = ctx.clone(); + move || { + ctx.request_repaint(); + } +} diff --git a/src/relay_view.rs b/src/relay_view.rs new file mode 100644 index 0000000..d7a1fe3 --- /dev/null +++ b/src/relay_view.rs @@ -0,0 +1,171 @@ +use crate::relay_pool_manager::{RelayPoolManager, RelayStatus}; +use egui::{Align, Button, Frame, Layout, Margin, Rgba, RichText, Rounding, Ui, Vec2}; + +use crate::app_style::NotedeckTextStyle; + +pub struct RelayView<'a> { + ctx: &'a egui::Context, + manager: RelayPoolManager<'a>, +} + +impl<'a> RelayView<'a> { + pub fn new(ctx: &'a egui::Context, manager: RelayPoolManager<'a>) -> Self { + RelayView { ctx, manager } + } + + pub fn panel(&'a mut self) { + let mut indices_to_remove: Option> = None; + + egui::CentralPanel::default().show(self.ctx, |ui| { + ui.add_space(24.0); + + ui.horizontal(|ui| { + ui.with_layout(Layout::left_to_right(Align::Center), |ui| { + ui.label( + RichText::new("Relays") + .text_style(NotedeckTextStyle::Heading2.text_style()), + ); + }); + + 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); + + egui::ScrollArea::vertical() + .scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden) + .auto_shrink([false; 2]) + .show(ui, |ui| { + indices_to_remove = self.show_relays(ui); + }); + }); + + if let Some(indices) = indices_to_remove { + self.manager.remove_relays(indices); + } + } + + /// Show the current relays, and returns the indices of relays the user requested to delete + fn show_relays(&'a self, ui: &mut Ui) -> Option> { + let mut indices_to_remove: Option> = None; + for (index, relay_info) in self.manager.get_relay_infos().iter().enumerate() { + ui.add_space(8.0); + ui.vertical_centered_justified(|ui| { + relay_frame(ui).show(ui, |ui| { + ui.horizontal(|ui| { + ui.with_layout(Layout::left_to_right(Align::Center), |ui| { + Frame::none() + // This frame is needed to add margin because the label will be added to the outer frame first and centered vertically before the connection status is added so the vertical centering isn't accurate. + // TODO: remove this hack and actually center the url & status at the same time + .inner_margin(Margin::symmetric(0.0, 4.0)) + .show(ui, |ui| { + egui::ScrollArea::horizontal() + .id_source(index) + .max_width( + ui.max_rect().width() + - get_right_side_width(relay_info.status), + ) // TODO: refactor to dynamically check the size of the 'right to left' portion and set the max width to be the screen width minus padding minus 'right to left' width + .show(ui, |ui| { + ui.label( + RichText::new(relay_info.relay_url) + .text_style( + NotedeckTextStyle::Monospace.text_style(), + ) + .color( + ui.style() + .visuals + .noninteractive() + .fg_stroke + .color, + ), + ); + }); + }); + }); + + ui.with_layout(Layout::right_to_left(Align::Center), |ui| { + if ui.add(delete_button(ui.visuals().dark_mode)).clicked() { + indices_to_remove.get_or_insert_with(Vec::new).push(index); + }; + + show_connection_status(ui, relay_info.status); + }); + }); + }); + }); + } + + indices_to_remove + } +} + +fn get_right_side_width(status: &RelayStatus) -> f32 { + match status { + RelayStatus::Connected => 150.0, + RelayStatus::Connecting => 160.0, + RelayStatus::Disconnected => 175.0, + } +} + +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 { + egui::include_image!("../assets/icons/delete_icon_4x.png") + } else { + // TODO: use light delete icon + egui::include_image!("../assets/icons/delete_icon_4x.png") + }; + + egui::Button::image(egui::Image::new(img_data).max_width(10.0)).frame(false) +} + +fn relay_frame(ui: &mut Ui) -> Frame { + Frame::none() + .inner_margin(Margin::same(8.0)) + .rounding(ui.style().noninteractive().rounding) + .stroke(ui.style().visuals.noninteractive().bg_stroke) +} + +fn show_connection_status(ui: &mut Ui, status: &RelayStatus) { + let fg_color = match status { + RelayStatus::Connected => ui.visuals().selection.bg_fill, + RelayStatus::Connecting => ui.visuals().warn_fg_color, + RelayStatus::Disconnected => ui.visuals().error_fg_color, + }; + let bg_color = egui::lerp(Rgba::from(fg_color)..=Rgba::BLACK, 0.8).into(); + + let label_text = match status { + RelayStatus::Connected => "Connected", + RelayStatus::Connecting => "Connecting...", + RelayStatus::Disconnected => "Not Connected", + }; + + let frame = Frame::none() + .rounding(Rounding::same(100.0)) + .fill(bg_color) + .inner_margin(Margin::symmetric(12.0, 4.0)); + + frame.show(ui, |ui| { + ui.label(RichText::new(label_text).color(fg_color)); + ui.add(get_connection_icon(status)); + }); +} + +fn get_connection_icon(status: &RelayStatus) -> egui::Image<'static> { + let img_data = match status { + RelayStatus::Connected => egui::include_image!("../assets/icons/connected_icon_4x.png"), + RelayStatus::Connecting => egui::include_image!("../assets/icons/connecting_icon_4x.png"), + RelayStatus::Disconnected => { + egui::include_image!("../assets/icons/disconnected_icon_4x.png") + } + }; + + egui::Image::new(img_data) +}