initial post reply view

Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin
2024-06-21 11:59:17 -07:00
parent ac0821db79
commit 0b3d6f7e37
8 changed files with 223 additions and 46 deletions

View File

@@ -1,10 +1,12 @@
pub mod contents;
pub mod options;
pub mod post;
pub mod reply;
pub use contents::NoteContents;
pub use options::NoteOptions;
pub use post::PostView;
pub use reply::PostReplyView;
use crate::{colors, notecache::CachedNote, ui, ui::View, Damus};
use egui::{Label, RichText, Sense};
@@ -128,6 +130,11 @@ impl<'a> Note<'a> {
self
}
pub fn medium_pfp(mut self, enable: bool) -> Self {
self.options_mut().set_medium_pfp(enable);
self
}
pub fn note_previews(mut self, enable: bool) -> Self {
self.options_mut().set_note_previews(enable);
self
@@ -179,6 +186,10 @@ impl<'a> Note<'a> {
.response
}
pub fn expand_size() -> f32 {
5.0
}
fn pfp(
&mut self,
note_key: NoteKey,
@@ -188,7 +199,9 @@ impl<'a> Note<'a> {
ui.spacing_mut().item_spacing.x = 16.0;
let pfp_size = if self.options().has_small_pfp() {
24.0
ui::ProfilePic::small_size()
} else if self.options().has_medium_pfp() {
ui::ProfilePic::medium_size()
} else {
ui::ProfilePic::default_size()
};
@@ -201,7 +214,6 @@ impl<'a> Note<'a> {
// these have different lifetimes and types,
// so the calls must be separate
Some(pic) => {
let expand_size = 5.0;
let anim_speed = 0.05;
let profile_key = profile.as_ref().unwrap().record().note_key();
let note_key = note_key.as_u64();
@@ -213,7 +225,7 @@ impl<'a> Note<'a> {
ui,
egui::Id::new((profile_key, note_key)),
pfp_size,
expand_size,
ui::Note::expand_size(),
anim_speed,
);

View File

@@ -8,7 +8,8 @@ bitflags! {
const actionbar = 0b00000001;
const note_previews = 0b00000010;
const small_pfp = 0b00000100;
const wide = 0b00001000;
const medium_pfp = 0b00001000;
const wide = 0b00010000;
}
}
@@ -28,6 +29,11 @@ impl NoteOptions {
(self & NoteOptions::small_pfp) == NoteOptions::small_pfp
}
#[inline]
pub fn has_medium_pfp(self) -> bool {
(self & NoteOptions::medium_pfp) == NoteOptions::medium_pfp
}
#[inline]
pub fn has_wide(self) -> bool {
(self & NoteOptions::wide) == NoteOptions::wide
@@ -41,6 +47,16 @@ impl NoteOptions {
*self &= !NoteOptions::small_pfp;
}
}
#[inline]
pub fn set_medium_pfp(&mut self, enable: bool) {
if enable {
*self |= NoteOptions::medium_pfp;
} else {
*self &= !NoteOptions::medium_pfp;
}
}
#[inline]
pub fn set_note_previews(&mut self, enable: bool) {
if enable {

View File

@@ -1,9 +1,9 @@
use crate::app::Damus;
use crate::draft::Draft;
use crate::ui;
use crate::ui::{Preview, PreviewConfig, View};
use egui::widgets::text_edit::TextEdit;
use nostrdb::Transaction;
use tracing::info;
pub struct PostView<'app, 'p> {
app: &'app mut Damus,
@@ -13,6 +13,20 @@ pub struct PostView<'app, 'p> {
replying_to: &'p [u8; 32],
}
pub struct NewPost {
pub content: String,
pub account: usize,
}
pub enum PostAction {
Post(NewPost),
}
pub struct PostResponse {
pub action: Option<PostAction>,
pub edit_response: egui::Response,
}
impl<'app, 'p> PostView<'app, 'p> {
pub fn new(app: &'app mut Damus, poster: usize, replying_to: &'p [u8; 32]) -> Self {
let id_source: Option<egui::Id> = None;
@@ -29,7 +43,14 @@ impl<'app, 'p> PostView<'app, 'p> {
self
}
fn editbox(&mut self, txn: &nostrdb::Transaction, ui: &mut egui::Ui) {
fn draft(&mut self) -> &mut Draft {
self.app
.drafts
.entry(enostr::NoteId::new(*self.replying_to))
.or_default()
}
fn editbox(&mut self, txn: &nostrdb::Transaction, ui: &mut egui::Ui) -> egui::Response {
ui.spacing_mut().item_spacing.x = 12.0;
let pfp_size = 24.0;
@@ -61,17 +82,13 @@ impl<'app, 'p> PostView<'app, 'p> {
);
}
let draft = self
.app
.drafts
.entry(enostr::NoteId::new(*self.replying_to))
.or_default();
let response = ui.add(TextEdit::multiline(&mut self.draft().buffer).frame(false));
let focused = ui
.add(TextEdit::multiline(&mut draft.buffer).frame(false))
.has_focus();
let focused = response.has_focus();
ui.ctx().data_mut(|d| d.insert_temp(self.id(), focused));
response
}
fn focused(&self, ui: &egui::Ui) -> bool {
@@ -83,7 +100,15 @@ impl<'app, 'p> PostView<'app, 'p> {
self.id_source.unwrap_or_else(|| egui::Id::new("post"))
}
pub fn ui(&mut self, txn: &nostrdb::Transaction, ui: &mut egui::Ui) {
pub fn outer_margin() -> f32 {
16.0
}
pub fn inner_margin() -> f32 {
12.0
}
pub fn ui(&mut self, txn: &nostrdb::Transaction, ui: &mut egui::Ui) -> PostResponse {
let focused = self.focused(ui);
let stroke = if focused {
ui.visuals().selection.stroke
@@ -93,8 +118,8 @@ impl<'app, 'p> PostView<'app, 'p> {
};
let mut frame = egui::Frame::default()
.inner_margin(egui::Margin::same(12.0))
.outer_margin(egui::Margin::same(12.0))
.inner_margin(egui::Margin::same(PostView::inner_margin()))
.outer_margin(egui::Margin::same(PostView::outer_margin()))
.fill(ui.visuals().extreme_bg_color)
.stroke(stroke)
.rounding(12.0);
@@ -108,22 +133,35 @@ impl<'app, 'p> PostView<'app, 'p> {
});
}
frame.show(ui, |ui| {
ui.vertical(|ui| {
ui.horizontal(|ui| {
self.editbox(txn, ui);
});
frame
.show(ui, |ui| {
ui.vertical(|ui| {
let edit_response = ui.horizontal(|ui| self.editbox(txn, ui)).inner;
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
if ui
.add_sized([91.0, 32.0], egui::Button::new("Post now"))
.clicked()
{
info!("Post clicked");
let action = ui
.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
if ui
.add_sized([91.0, 32.0], egui::Button::new("Post now"))
.clicked()
{
Some(PostAction::Post(NewPost {
content: self.draft().buffer.clone(),
account: self.poster,
}))
} else {
None
}
})
.inner;
PostResponse {
action,
edit_response,
}
});
});
});
})
.inner
})
.inner
}
}

View File

@@ -1 +1,106 @@
struct PostReplyView {}
use crate::{ui, Damus};
pub struct PostReplyView<'a> {
app: &'a mut Damus,
id_source: Option<egui::Id>,
note: &'a nostrdb::Note<'a>,
}
impl<'a> PostReplyView<'a> {
pub fn new(app: &'a mut Damus, note: &'a nostrdb::Note<'a>) -> Self {
let id_source: Option<egui::Id> = None;
PostReplyView {
app,
id_source,
note,
}
}
pub fn id_source(mut self, id: egui::Id) -> Self {
self.id_source = Some(id);
self
}
pub fn id(&self) -> egui::Id {
self.id_source
.unwrap_or_else(|| egui::Id::new("post-reply-view"))
}
pub fn show(&mut self, ui: &mut egui::Ui) {
ui.vertical(|ui| {
let avail_rect = ui.available_rect_before_wrap();
// This is the offset of the post view's pfp. We use this
// to indent things so that the reply line is aligned
let pfp_offset = ui::PostView::outer_margin()
+ ui::PostView::inner_margin()
+ ui::ProfilePic::small_size() / 2.0;
let note_offset =
pfp_offset - ui::ProfilePic::medium_size() / 2.0 - ui::Note::expand_size() / 2.0;
egui::Frame::none()
.outer_margin(egui::Margin::same(note_offset))
.show(ui, |ui| {
ui::Note::new(self.app, self.note)
.actionbar(false)
.medium_pfp(true)
.show(ui);
});
let poster = self
.app
.account_manager
.get_selected_account_index()
.unwrap_or(0);
let replying_to = self.note.pubkey();
let rect_before_post = ui.min_rect();
let id = self.id();
let post_response = ui::PostView::new(self.app, poster, replying_to)
.id_source(id)
.ui(self.note.txn().unwrap(), ui);
//
// reply line
//
// Position and draw the reply line
let mut rect = ui.min_rect();
// Position the line right above the poster's profile pic in
// the post box. Use the PostView's margin values to
// determine this offset.
rect.min.x = avail_rect.min.x + pfp_offset;
// honestly don't know what the fuck I'm doing here. just trying
// to get the line under the profile picture
rect.min.y = avail_rect.min.y
+ (ui::ProfilePic::medium_size() / 2.0
+ ui::ProfilePic::medium_size()
+ ui::Note::expand_size() * 2.0)
+ 1.0;
// For some reason we need to nudge the reply line's height a
// few more pixels?
let nudge = if post_response.edit_response.has_focus() {
// we nudge by one less pixel if focused, otherwise it
// overlaps the focused PostView purple border color
2.0
} else {
// we have to nudge by one more pixel when not focused
// otherwise it looks like there's a gap(?)
3.0
};
rect.max.y = rect_before_post.max.y + ui::PostView::outer_margin() + nudge;
ui.painter().vline(
rect.left(),
rect.y_range(),
ui.visuals().widgets.noninteractive.bg_stroke,
);
});
}
}