relay.rs (7809B)
1 use crate::relay_pool_manager::{RelayPoolManager, RelayStatus}; 2 use crate::ui::{Preview, PreviewConfig, View}; 3 use egui::{Align, Button, Frame, Layout, Margin, Rgba, RichText, Rounding, Ui, Vec2}; 4 5 use crate::app_style::NotedeckTextStyle; 6 use enostr::RelayPool; 7 8 pub struct RelayView<'a> { 9 manager: RelayPoolManager<'a>, 10 } 11 12 impl<'a> View for RelayView<'a> { 13 fn ui(&mut self, ui: &mut egui::Ui) { 14 ui.add_space(24.0); 15 16 ui.horizontal(|ui| { 17 ui.with_layout(Layout::left_to_right(Align::Center), |ui| { 18 ui.label( 19 RichText::new("Relays").text_style(NotedeckTextStyle::Heading2.text_style()), 20 ); 21 }); 22 23 ui.with_layout(Layout::right_to_left(Align::Center), |ui| { 24 if ui.add(add_relay_button()).clicked() { 25 // TODO: navigate to 'add relay view' 26 }; 27 }); 28 }); 29 30 ui.add_space(8.0); 31 32 egui::ScrollArea::vertical() 33 .scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden) 34 .auto_shrink([false; 2]) 35 .show(ui, |ui| { 36 if let Some(indices) = self.show_relays(ui) { 37 self.manager.remove_relays(indices); 38 } 39 }); 40 } 41 } 42 43 impl<'a> RelayView<'a> { 44 pub fn new(manager: RelayPoolManager<'a>) -> Self { 45 RelayView { manager } 46 } 47 48 pub fn panel(&mut self, ui: &mut egui::Ui) { 49 egui::CentralPanel::default().show(ui.ctx(), |ui| self.ui(ui)); 50 } 51 52 /// Show the current relays, and returns the indices of relays the user requested to delete 53 fn show_relays(&'a self, ui: &mut Ui) -> Option<Vec<usize>> { 54 let mut indices_to_remove: Option<Vec<usize>> = None; 55 for (index, relay_info) in self.manager.get_relay_infos().iter().enumerate() { 56 ui.add_space(8.0); 57 ui.vertical_centered_justified(|ui| { 58 relay_frame(ui).show(ui, |ui| { 59 ui.horizontal(|ui| { 60 ui.with_layout(Layout::left_to_right(Align::Center), |ui| { 61 Frame::none() 62 // 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. 63 // TODO: remove this hack and actually center the url & status at the same time 64 .inner_margin(Margin::symmetric(0.0, 4.0)) 65 .show(ui, |ui| { 66 egui::ScrollArea::horizontal() 67 .id_source(index) 68 .max_width( 69 ui.max_rect().width() 70 - get_right_side_width(relay_info.status), 71 ) // 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 72 .show(ui, |ui| { 73 ui.label( 74 RichText::new(relay_info.relay_url) 75 .text_style( 76 NotedeckTextStyle::Monospace.text_style(), 77 ) 78 .color( 79 ui.style() 80 .visuals 81 .noninteractive() 82 .fg_stroke 83 .color, 84 ), 85 ); 86 }); 87 }); 88 }); 89 90 ui.with_layout(Layout::right_to_left(Align::Center), |ui| { 91 if ui.add(delete_button(ui.visuals().dark_mode)).clicked() { 92 indices_to_remove.get_or_insert_with(Vec::new).push(index); 93 }; 94 95 show_connection_status(ui, relay_info.status); 96 }); 97 }); 98 }); 99 }); 100 } 101 102 indices_to_remove 103 } 104 } 105 106 fn get_right_side_width(status: &RelayStatus) -> f32 { 107 match status { 108 RelayStatus::Connected => 150.0, 109 RelayStatus::Connecting => 160.0, 110 RelayStatus::Disconnected => 175.0, 111 } 112 } 113 114 fn add_relay_button() -> egui::Button<'static> { 115 Button::new("+ Add relay").min_size(Vec2::new(0.0, 32.0)) 116 } 117 118 fn delete_button(_dark_mode: bool) -> egui::Button<'static> { 119 /* 120 let img_data = if dark_mode { 121 egui::include_image!("../../assets/icons/delete_icon_4x.png") 122 } else { 123 // TODO: use light delete icon 124 egui::include_image!("../../assets/icons/delete_icon_4x.png") 125 }; 126 */ 127 let img_data = egui::include_image!("../../assets/icons/delete_icon_4x.png"); 128 129 egui::Button::image(egui::Image::new(img_data).max_width(10.0)).frame(false) 130 } 131 132 fn relay_frame(ui: &mut Ui) -> Frame { 133 Frame::none() 134 .inner_margin(Margin::same(8.0)) 135 .rounding(ui.style().noninteractive().rounding) 136 .stroke(ui.style().visuals.noninteractive().bg_stroke) 137 } 138 139 fn show_connection_status(ui: &mut Ui, status: &RelayStatus) { 140 let fg_color = match status { 141 RelayStatus::Connected => ui.visuals().selection.bg_fill, 142 RelayStatus::Connecting => ui.visuals().warn_fg_color, 143 RelayStatus::Disconnected => ui.visuals().error_fg_color, 144 }; 145 let bg_color = egui::lerp(Rgba::from(fg_color)..=Rgba::BLACK, 0.8).into(); 146 147 let label_text = match status { 148 RelayStatus::Connected => "Connected", 149 RelayStatus::Connecting => "Connecting...", 150 RelayStatus::Disconnected => "Not Connected", 151 }; 152 153 let frame = Frame::none() 154 .rounding(Rounding::same(100.0)) 155 .fill(bg_color) 156 .inner_margin(Margin::symmetric(12.0, 4.0)); 157 158 frame.show(ui, |ui| { 159 ui.label(RichText::new(label_text).color(fg_color)); 160 ui.add(get_connection_icon(status)); 161 }); 162 } 163 164 fn get_connection_icon(status: &RelayStatus) -> egui::Image<'static> { 165 let img_data = match status { 166 RelayStatus::Connected => egui::include_image!("../../assets/icons/connected_icon_4x.png"), 167 RelayStatus::Connecting => { 168 egui::include_image!("../../assets/icons/connecting_icon_4x.png") 169 } 170 RelayStatus::Disconnected => { 171 egui::include_image!("../../assets/icons/disconnected_icon_4x.png") 172 } 173 }; 174 175 egui::Image::new(img_data) 176 } 177 178 // PREVIEWS 179 180 mod preview { 181 use super::*; 182 use crate::test_data::sample_pool; 183 184 pub struct RelayViewPreview { 185 pool: RelayPool, 186 } 187 188 impl RelayViewPreview { 189 fn new() -> Self { 190 RelayViewPreview { 191 pool: sample_pool(), 192 } 193 } 194 } 195 196 impl View for RelayViewPreview { 197 fn ui(&mut self, ui: &mut egui::Ui) { 198 self.pool.try_recv(); 199 RelayView::new(RelayPoolManager::new(&mut self.pool)).ui(ui); 200 } 201 } 202 203 impl<'a> Preview for RelayView<'a> { 204 type Prev = RelayViewPreview; 205 206 fn preview(_cfg: PreviewConfig) -> Self::Prev { 207 RelayViewPreview::new() 208 } 209 } 210 }