context: implement note broadcasting

Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin
2025-04-10 10:39:40 -07:00
parent 956c557851
commit 50dec5b5d5
11 changed files with 150 additions and 97 deletions

View File

@@ -4,7 +4,7 @@ use crate::ui::{
};
use crate::{actionbar::NoteAction, timeline::TimelineKind};
use egui::{Button, Color32, Hyperlink, Image, Response, RichText, Sense, Window};
use enostr::KeypairUnowned;
use enostr::{KeypairUnowned, RelayPool};
use nostrdb::{BlockType, Mention, Ndb, Note, NoteKey, Transaction};
use notedeck_ui::images::ImageType;
use notedeck_ui::{
@@ -22,6 +22,7 @@ pub struct NoteContext<'d> {
pub img_cache: &'d mut Images,
pub note_cache: &'d mut NoteCache,
pub zaps: &'d mut Zaps,
pub pool: &'d mut RelayPool,
}
pub struct NoteContents<'a, 'd> {

View File

@@ -1,20 +1,25 @@
use egui::{Rect, Vec2};
use enostr::{NoteId, Pubkey};
use enostr::{ClientMessage, NoteId, Pubkey, RelayPool};
use nostrdb::{Note, NoteKey};
use tracing::error;
#[derive(Clone)]
#[derive(Debug, Clone, Eq, PartialEq)]
#[allow(clippy::enum_variant_names)]
pub enum NoteContextSelection {
CopyText,
CopyPubkey,
CopyNoteId,
CopyNoteJSON,
Broadcast,
}
impl NoteContextSelection {
pub fn process(&self, ui: &mut egui::Ui, note: &Note<'_>) {
pub fn process(&self, ui: &mut egui::Ui, note: &Note<'_>, pool: &mut RelayPool) {
match self {
NoteContextSelection::Broadcast => {
tracing::info!("Broadcasting note {}", hex::encode(note.id()));
pool.send(&ClientMessage::event(note).unwrap());
}
NoteContextSelection::CopyText => {
ui.ctx().copy_text(note.content().to_string());
}
@@ -161,6 +166,10 @@ impl NoteContextButton {
context_selection = Some(NoteContextSelection::CopyNoteJSON);
ui.close_menu();
}
if ui.button("Broadcast").clicked() {
context_selection = Some(NoteContextSelection::Broadcast);
ui.close_menu();
}
});
context_selection

View File

@@ -10,13 +10,13 @@ pub use contents::NoteContents;
use contents::NoteContext;
pub use context::{NoteContextButton, NoteContextSelection};
pub use options::NoteOptions;
pub use post::{PostAction, PostResponse, PostType, PostView};
pub use post::{NewPostAction, PostAction, PostResponse, PostType, PostView};
pub use quote_repost::QuoteRepostView;
pub use reply::PostReplyView;
pub use reply_description::reply_desc;
use crate::{
actionbar::{NoteAction, ZapAction},
actionbar::{ContextSelection, NoteAction, ZapAction},
profile::get_display_name,
timeline::{ThreadSelection, TimelineKind},
ui::{self, View},
@@ -43,7 +43,6 @@ pub struct NoteView<'a, 'd> {
pub struct NoteResponse {
pub response: egui::Response,
pub context_selection: Option<NoteContextSelection>,
pub action: Option<NoteAction>,
}
@@ -51,7 +50,6 @@ impl NoteResponse {
pub fn new(response: egui::Response) -> Self {
Self {
response,
context_selection: None,
action: None,
}
}
@@ -60,11 +58,6 @@ impl NoteResponse {
self.action = action;
self
}
pub fn select_option(mut self, context_selection: Option<NoteContextSelection>) -> Self {
self.context_selection = context_selection;
self
}
}
impl View for NoteView<'_, '_> {
@@ -338,7 +331,6 @@ impl<'a, 'd> NoteView<'a, 'd> {
let txn = self.note.txn().expect("todo: support non-db notes");
let mut note_action: Option<NoteAction> = None;
let mut selected_option: Option<NoteContextSelection> = None;
let hitbox_id = note_hitbox_id(note_key, self.options(), self.parent);
let profile = self
@@ -505,7 +497,9 @@ impl<'a, 'd> NoteView<'a, 'd> {
};
let resp = ui.add(NoteContextButton::new(note_key).place_at(context_pos));
selected_option = NoteContextButton::menu(ui, resp.clone());
if let Some(action) = NoteContextButton::menu(ui, resp.clone()) {
note_action = Some(NoteAction::Context(ContextSelection { note_key, action }));
}
}
let note_action = if note_hitbox_clicked(ui, hitbox_id, &response.rect, maybe_hitbox) {
@@ -523,9 +517,7 @@ impl<'a, 'd> NoteView<'a, 'd> {
note_action
};
NoteResponse::new(response)
.with_action(note_action)
.select_option(selected_option)
NoteResponse::new(response).with_action(note_action)
}
}

View File

@@ -1,3 +1,4 @@
use crate::actionbar::NoteAction;
use crate::draft::{Draft, Drafts, MentionHint};
use crate::media_upload::{nostrbuild_nip96_upload, MediaPath};
use crate::post::{downcast_post_buffer, MentionType, NewPost};
@@ -20,7 +21,6 @@ use notedeck::supported_mime_hosted_at_url;
use tracing::error;
use super::contents::{render_note_preview, NoteContext};
use super::NoteContextSelection;
use super::NoteOptions;
pub struct PostView<'a, 'd> {
@@ -40,14 +40,22 @@ pub enum PostType {
Reply(NoteId),
}
pub struct PostAction {
pub enum PostAction {
/// The NoteAction on a note you are replying to.
QuotedNoteAction(NoteAction),
/// The reply/new post action
NewPostAction(NewPostAction),
}
pub struct NewPostAction {
post_type: PostType,
post: NewPost,
}
impl PostAction {
impl NewPostAction {
pub fn new(post_type: PostType, post: NewPost) -> Self {
PostAction { post_type, post }
NewPostAction { post_type, post }
}
pub fn execute(
@@ -73,7 +81,7 @@ impl PostAction {
}
};
pool.send(&enostr::ClientMessage::event(note)?);
pool.send(&enostr::ClientMessage::event(&note)?);
drafts.get_from_post_type(&self.post_type).clear();
Ok(())
@@ -83,7 +91,6 @@ impl PostAction {
pub struct PostResponse {
pub action: Option<PostAction>,
pub edit_response: egui::Response,
pub context_selection: Option<NoteContextSelection>,
}
impl<'a, 'd> PostView<'a, 'd> {
@@ -321,32 +328,34 @@ impl<'a, 'd> PostView<'a, 'd> {
.show(ui, |ui| {
ui.vertical(|ui| {
let edit_response = ui.horizontal(|ui| self.editbox(txn, ui)).inner;
let mut context_selection = None;
if let PostType::Quote(id) = self.post_type {
let note_response = if let PostType::Quote(id) = self.post_type {
let avail_size = ui.available_size_before_wrap();
ui.with_layout(Layout::left_to_right(egui::Align::TOP), |ui| {
context_selection = Frame::NONE
.show(ui, |ui| {
ui.vertical(|ui| {
ui.set_max_width(avail_size.x * 0.8);
let resp = render_note_preview(
ui,
self.note_context,
&Some(self.poster.into()),
txn,
id.bytes(),
nostrdb::NoteKey::new(0),
self.note_options,
);
resp
Some(
ui.with_layout(Layout::left_to_right(egui::Align::TOP), |ui| {
Frame::NONE
.show(ui, |ui| {
ui.vertical(|ui| {
ui.set_max_width(avail_size.x * 0.8);
render_note_preview(
ui,
self.note_context,
&Some(self.poster.into()),
txn,
id.bytes(),
nostrdb::NoteKey::new(0),
self.note_options,
)
})
.inner
})
.inner
.context_selection
})
.inner;
});
}
})
.inner,
)
} else {
None
};
Frame::new()
.inner_margin(Margin::symmetric(0, 8))
@@ -362,7 +371,7 @@ impl<'a, 'd> PostView<'a, 'd> {
self.transfer_uploads(ui);
self.show_upload_errors(ui);
let action = ui
let post_action = ui
.horizontal(|ui| {
ui.with_layout(
egui::Layout::left_to_right(egui::Align::BOTTOM),
@@ -394,7 +403,7 @@ impl<'a, 'd> PostView<'a, 'd> {
self.draft.uploaded_media.clone(),
output.mentions,
);
Some(PostAction::new(self.post_type.clone(), new_post))
Some(NewPostAction::new(self.post_type.clone(), new_post))
} else {
None
}
@@ -403,10 +412,13 @@ impl<'a, 'd> PostView<'a, 'd> {
})
.inner;
let action = note_response
.and_then(|nr| nr.action.map(PostAction::QuotedNoteAction))
.or(post_action.map(PostAction::NewPostAction));
PostResponse {
action,
edit_response,
context_selection,
}
})
.inner
@@ -736,6 +748,7 @@ mod preview {
img_cache: app.img_cache,
note_cache: app.note_cache,
zaps: app.zaps,
pool: app.pool,
};
PostView::new(

View File

@@ -1,6 +1,6 @@
use crate::draft::Draft;
use crate::ui;
use crate::ui::note::{PostResponse, PostType};
use crate::ui::note::{PostAction, PostResponse, PostType};
use enostr::{FilledKeypair, NoteId};
use super::contents::NoteContext;
@@ -61,7 +61,7 @@ impl<'a, 'd> PostReplyView<'a, 'd> {
let note_offset: i8 =
pfp_offset - ui::ProfilePic::medium_size() / 2 - ui::NoteView::expand_size() / 2;
let selection = egui::Frame::NONE
let quoted_note = egui::Frame::NONE
.outer_margin(egui::Margin::same(note_offset))
.show(ui, |ui| {
ui::NoteView::new(
@@ -75,8 +75,7 @@ impl<'a, 'd> PostReplyView<'a, 'd> {
.options_button(true)
.show(ui)
})
.inner
.context_selection;
.inner;
let id = self.id();
let replying_to = self.note.id();
@@ -95,7 +94,9 @@ impl<'a, 'd> PostReplyView<'a, 'd> {
.ui(self.note.txn().unwrap(), ui)
};
post_response.context_selection = selection;
post_response.action = post_response
.action
.or(quoted_note.action.map(PostAction::QuotedNoteAction));
//
// reply line

View File

@@ -407,10 +407,6 @@ impl<'a, 'd> TimelineTabView<'a, 'd> {
if let Some(note_action) = resp.action {
action = Some(note_action)
}
if let Some(context) = resp.context_selection {
context.process(ui, &note);
}
});
ui::hline(ui);