post.rs (5607B)
1 use crate::app::Damus; 2 use crate::draft::{Draft, DraftSource}; 3 use crate::post::NewPost; 4 use crate::ui; 5 use crate::ui::{Preview, PreviewConfig, View}; 6 use egui::widgets::text_edit::TextEdit; 7 use nostrdb::Transaction; 8 9 pub struct PostView<'app, 'd> { 10 app: &'app mut Damus, 11 /// account index 12 poster: usize, 13 draft_source: DraftSource<'d>, 14 id_source: Option<egui::Id>, 15 } 16 17 pub enum PostAction { 18 Post(NewPost), 19 } 20 21 pub struct PostResponse { 22 pub action: Option<PostAction>, 23 pub edit_response: egui::Response, 24 } 25 26 impl<'app, 'd> PostView<'app, 'd> { 27 pub fn new(app: &'app mut Damus, draft_source: DraftSource<'d>, poster: usize) -> Self { 28 let id_source: Option<egui::Id> = None; 29 PostView { 30 id_source, 31 app, 32 poster, 33 draft_source, 34 } 35 } 36 37 pub fn id_source(mut self, id_source: impl std::hash::Hash) -> Self { 38 self.id_source = Some(egui::Id::new(id_source)); 39 self 40 } 41 42 fn draft(&mut self) -> &mut Draft { 43 self.draft_source.draft(&mut self.app.drafts) 44 } 45 46 fn editbox(&mut self, txn: &nostrdb::Transaction, ui: &mut egui::Ui) -> egui::Response { 47 ui.spacing_mut().item_spacing.x = 12.0; 48 49 let pfp_size = 24.0; 50 51 let poster_pubkey = self 52 .app 53 .account_manager 54 .get_account(self.poster) 55 .map(|acc| acc.pubkey.bytes()) 56 .unwrap_or(crate::test_data::test_pubkey()); 57 58 // TODO: refactor pfp control to do all of this for us 59 let poster_pfp = self 60 .app 61 .ndb 62 .get_profile_by_pubkey(txn, poster_pubkey) 63 .as_ref() 64 .ok() 65 .and_then(|p| { 66 Some(ui::ProfilePic::from_profile(&mut self.app.img_cache, p)?.size(pfp_size)) 67 }); 68 69 if let Some(pfp) = poster_pfp { 70 ui.add(pfp); 71 } else { 72 ui.add( 73 ui::ProfilePic::new(&mut self.app.img_cache, ui::ProfilePic::no_pfp_url()) 74 .size(pfp_size), 75 ); 76 } 77 78 let buffer = &mut self.draft_source.draft(&mut self.app.drafts).buffer; 79 80 let response = ui.add_sized( 81 ui.available_size(), 82 TextEdit::multiline(buffer) 83 .hint_text(egui::RichText::new("Write a banger note here...").weak()) 84 .frame(false), 85 ); 86 87 let focused = response.has_focus(); 88 89 ui.ctx().data_mut(|d| d.insert_temp(self.id(), focused)); 90 91 response 92 } 93 94 fn focused(&self, ui: &egui::Ui) -> bool { 95 ui.ctx() 96 .data(|d| d.get_temp::<bool>(self.id()).unwrap_or(false)) 97 } 98 99 fn id(&self) -> egui::Id { 100 self.id_source.unwrap_or_else(|| egui::Id::new("post")) 101 } 102 103 pub fn outer_margin() -> f32 { 104 16.0 105 } 106 107 pub fn inner_margin() -> f32 { 108 12.0 109 } 110 111 pub fn ui(&mut self, txn: &nostrdb::Transaction, ui: &mut egui::Ui) -> PostResponse { 112 let focused = self.focused(ui); 113 let stroke = if focused { 114 ui.visuals().selection.stroke 115 } else { 116 //ui.visuals().selection.stroke 117 ui.visuals().noninteractive().bg_stroke 118 }; 119 120 let mut frame = egui::Frame::default() 121 .inner_margin(egui::Margin::same(PostView::inner_margin())) 122 .outer_margin(egui::Margin::same(PostView::outer_margin())) 123 .fill(ui.visuals().extreme_bg_color) 124 .stroke(stroke) 125 .rounding(12.0); 126 127 if focused { 128 frame = frame.shadow(egui::epaint::Shadow { 129 offset: egui::vec2(0.0, 0.0), 130 blur: 8.0, 131 spread: 0.0, 132 color: stroke.color, 133 }); 134 } 135 136 frame 137 .show(ui, |ui| { 138 ui.vertical(|ui| { 139 let edit_response = ui.horizontal(|ui| self.editbox(txn, ui)).inner; 140 141 let action = ui 142 .with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| { 143 if ui 144 .add_sized([91.0, 32.0], egui::Button::new("Post now")) 145 .clicked() 146 { 147 Some(PostAction::Post(NewPost { 148 content: self.draft().buffer.clone(), 149 account: self.poster, 150 })) 151 } else { 152 None 153 } 154 }) 155 .inner; 156 157 PostResponse { 158 action, 159 edit_response, 160 } 161 }) 162 .inner 163 }) 164 .inner 165 } 166 } 167 168 mod preview { 169 use super::*; 170 use crate::test_data; 171 172 pub struct PostPreview { 173 app: Damus, 174 } 175 176 impl PostPreview { 177 fn new(is_mobile: bool) -> Self { 178 PostPreview { 179 app: test_data::test_app(is_mobile), 180 } 181 } 182 } 183 184 impl View for PostPreview { 185 fn ui(&mut self, ui: &mut egui::Ui) { 186 let txn = Transaction::new(&self.app.ndb).unwrap(); 187 PostView::new(&mut self.app, DraftSource::Compose, 0).ui(&txn, ui); 188 } 189 } 190 191 impl<'app, 'p> Preview for PostView<'app, 'p> { 192 type Prev = PostPreview; 193 194 fn preview(cfg: PreviewConfig) -> Self::Prev { 195 PostPreview::new(cfg.is_mobile) 196 } 197 } 198 }