reply.rs (5873B)
1 use crate::draft::Draft; 2 use crate::ui::{ 3 self, 4 note::{PostAction, PostResponse, PostType}, 5 }; 6 7 use egui::{Rect, Response, ScrollArea, Ui}; 8 use enostr::{FilledKeypair, NoteId}; 9 use notedeck::{DragResponse, NoteContext}; 10 use notedeck_ui::{NoteOptions, NoteView, ProfilePic}; 11 12 pub struct PostReplyView<'a, 'd> { 13 note_context: &'a mut NoteContext<'d>, 14 poster: FilledKeypair<'a>, 15 draft: &'a mut Draft, 16 note: &'a nostrdb::Note<'a>, 17 scroll_id: egui::Id, 18 inner_rect: egui::Rect, 19 note_options: NoteOptions, 20 } 21 22 impl<'a, 'd> PostReplyView<'a, 'd> { 23 #[allow(clippy::too_many_arguments)] 24 pub fn new( 25 note_context: &'a mut NoteContext<'d>, 26 poster: FilledKeypair<'a>, 27 draft: &'a mut Draft, 28 note: &'a nostrdb::Note<'a>, 29 inner_rect: egui::Rect, 30 note_options: NoteOptions, 31 col: usize, 32 ) -> Self { 33 PostReplyView { 34 note_context, 35 poster, 36 draft, 37 note, 38 scroll_id: PostReplyView::scroll_id(col, note.id()), 39 inner_rect, 40 note_options, 41 } 42 } 43 44 fn id(col: usize, note_id: &[u8; 32]) -> egui::Id { 45 egui::Id::new(("reply_view", col, note_id)) 46 } 47 48 pub fn scroll_id(col: usize, note_id: &[u8; 32]) -> egui::Id { 49 PostReplyView::id(col, note_id).with("scroll") 50 } 51 52 pub fn show(&mut self, ui: &mut egui::Ui) -> DragResponse<PostResponse> { 53 let scroll_out = ScrollArea::vertical() 54 .id_salt(self.scroll_id) 55 .stick_to_bottom(true) 56 .show(ui, |ui| Some(self.show_internal(ui))); 57 58 let scroll_id = scroll_out.id; 59 if let Some(inner) = scroll_out.inner { 60 inner 61 } else { 62 DragResponse::none() 63 } 64 .scroll_raw(scroll_id) 65 } 66 67 // no scroll 68 fn show_internal(&mut self, ui: &mut egui::Ui) -> DragResponse<PostResponse> { 69 ui.vertical(|ui| { 70 let avail_rect = ui.available_rect_before_wrap(); 71 72 // This is the offset of the post view's pfp. We use this 73 // to indent things so that the reply line is aligned 74 let pfp_offset: i8 = ui::PostView::outer_margin() 75 + ui::PostView::inner_margin() 76 + ProfilePic::small_size() / 2; 77 78 let note_offset: i8 = 79 pfp_offset - ProfilePic::medium_size() / 2 - NoteView::expand_size() / 2; 80 81 let quoted_note = egui::Frame::NONE 82 .outer_margin(egui::Margin::same(note_offset)) 83 .show(ui, |ui| { 84 NoteView::new(self.note_context, self.note, self.note_options) 85 .truncate(false) 86 .selectable_text(true) 87 .actionbar(false) 88 .medium_pfp(true) 89 .options_button(true) 90 .show(ui) 91 }) 92 .inner; 93 94 let replying_to = self.note.id(); 95 let rect_before_post = ui.min_rect(); 96 97 let mut post_response = { 98 ui::PostView::new( 99 self.note_context, 100 self.draft, 101 PostType::Reply(NoteId::new(*replying_to)), 102 self.poster, 103 self.inner_rect, 104 self.note_options, 105 ) 106 .ui_no_scroll(self.note.txn().unwrap(), ui) 107 }; 108 109 post_response = post_response.map_output(|mut o| { 110 o.action = o 111 .action 112 .or(quoted_note.action.map(PostAction::QuotedNoteAction)); 113 o 114 }); 115 116 if let Some(p) = &post_response.output { 117 reply_line_ui( 118 &rect_before_post, 119 &p.edit_response, 120 pfp_offset as f32, 121 &avail_rect, 122 ui, 123 ); 124 } 125 126 // 127 // NOTE(jb55): We add some space so that you can scroll to 128 // put the input box higher. This can happen in some 129 // situations where the input box gets covered or if its too 130 // large and things start breaking. I think this is an ok 131 // solution but there could be a better one. 132 // 133 //ui.add_space(500.0); 134 135 post_response 136 }) 137 .inner 138 } 139 } 140 141 /// The vertical line in the reply view 142 fn reply_line_ui( 143 rect_before_post: &Rect, 144 edit_response: &Response, 145 pfp_offset: f32, 146 avail_rect: &Rect, 147 ui: &mut Ui, 148 ) { 149 // Position and draw the reply line 150 let mut rect = ui.min_rect(); 151 152 // Position the line right above the poster's profile pic in 153 // the post box. Use the PostView's margin values to 154 // determine this offset. 155 rect.min.x = avail_rect.min.x + pfp_offset; 156 157 // honestly don't know what the fuck I'm doing here. just trying 158 // to get the line under the profile picture 159 rect.min.y = avail_rect.min.y 160 + (ProfilePic::medium_size() as f32 / 2.0 161 + ProfilePic::medium_size() as f32 162 + NoteView::expand_size() as f32 * 2.0) 163 + 1.0; 164 165 // For some reason we need to nudge the reply line's height a 166 // few more pixels? 167 let nudge = if edit_response.has_focus() { 168 // we nudge by one less pixel if focused, otherwise it 169 // overlaps the focused PostView purple border color 170 2.0 171 } else { 172 // we have to nudge by one more pixel when not focused 173 // otherwise it looks like there's a gap(?) 174 3.0 175 }; 176 177 rect.max.y = rect_before_post.max.y + ui::PostView::outer_margin() as f32 + nudge; 178 179 ui.painter().vline( 180 rect.left(), 181 rect.y_range(), 182 ui.visuals().widgets.noninteractive.bg_stroke, 183 ); 184 }