Skip to content

Commit

Permalink
Merge 'Initial quote reposts #305'
Browse files Browse the repository at this point in the history
kernelkind (6):
      post quote reposts impl
      make PostActionExecutor for code reuse
      add repost button
      address PR comments
      make views pure
      minor cleanup
  • Loading branch information
jb55 committed Sep 18, 2024
2 parents 8e32f75 + 0a08ae9 commit 2208e68
Show file tree
Hide file tree
Showing 14 changed files with 296 additions and 74 deletions.
6 changes: 6 additions & 0 deletions enostr/src/note.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use std::hash::{Hash, Hasher};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct NoteId([u8; 32]);

static HRP_NOTE: nostr::bech32::Hrp = nostr::bech32::Hrp::parse_unchecked("note");

impl NoteId {
pub fn new(bytes: [u8; 32]) -> Self {
NoteId(bytes)
Expand All @@ -23,6 +25,10 @@ impl NoteId {
let evid = NoteId(hex::decode(hex_str)?.as_slice().try_into().unwrap());
Ok(evid)
}

pub fn to_bech(&self) -> Option<String> {
nostr::bech32::encode::<nostr::bech32::Bech32>(HRP_NOTE, &self.0).ok()
}
}

/// Event is the struct used to represent a Nostr event
Expand Down
7 changes: 7 additions & 0 deletions src/actionbar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use uuid::Uuid;
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum BarAction {
Reply(NoteId),
Quote(NoteId),
OpenThread(NoteId),
}

Expand Down Expand Up @@ -130,6 +131,12 @@ impl BarAction {
BarAction::OpenThread(note_id) => {
open_thread(ndb, txn, router, note_cache, pool, threads, note_id.bytes())
}

BarAction::Quote(note_id) => {
router.route_to(Route::quote(note_id));
router.navigating = true;
None
}
}
}

Expand Down
8 changes: 7 additions & 1 deletion src/draft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub struct Draft {
#[derive(Default)]
pub struct Drafts {
replies: HashMap<[u8; 32], Draft>,
quotes: HashMap<[u8; 32], Draft>,
compose: Draft,
}

Expand All @@ -19,14 +20,19 @@ impl Drafts {
pub fn reply_mut(&mut self, id: &[u8; 32]) -> &mut Draft {
self.replies.entry(*id).or_default()
}

pub fn quote_mut(&mut self, id: &[u8; 32]) -> &mut Draft {
self.quotes.entry(*id).or_default()
}
}

/*
pub enum DraftSource<'a> {
Compose,
Reply(&'a [u8; 32]), // note id
Quote(&'a [u8; 32]), // note id
}

/*
impl<'a> DraftSource<'a> {
pub fn draft(&self, drafts: &'a mut Drafts) -> &'a mut Draft {
match self {
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ mod nav;
mod note;
mod notecache;
mod post;
mod post_action_executor;
mod profile;
pub mod relay_pool_manager;
mod result;
Expand Down
21 changes: 21 additions & 0 deletions src/post.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,25 @@ impl NewPost {
.build()
.expect("expected build to work")
}

pub fn to_quote(&self, seckey: &[u8; 32], quoting: &Note) -> Note {
let new_content = format!(
"{}\nnostr:{}",
self.content,
enostr::NoteId::new(*quoting.id()).to_bech().unwrap()
);

NoteBuilder::new()
.kind(1)
.content(&new_content)
.start_tag()
.tag_str("q")
.tag_str(&hex::encode(quoting.id()))
.start_tag()
.tag_str("p")
.tag_str(&hex::encode(quoting.pubkey()))
.sign(seckey)
.build()
.expect("expected build to work")
}
}
28 changes: 28 additions & 0 deletions src/post_action_executor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use enostr::{FilledKeypair, RelayPool};
use nostrdb::Note;
use tracing::info;

use crate::{draft::Draft, post::NewPost, ui::note::PostAction};

pub struct PostActionExecutor {}

impl PostActionExecutor {
pub fn execute<'a>(
poster: FilledKeypair<'_>,
action: &'a PostAction,
pool: &mut RelayPool,
draft: &mut Draft,
get_note: impl Fn(&'a NewPost, &[u8; 32]) -> Note<'a>,
) {
match action {
PostAction::Post(np) => {
let note = get_note(np, &poster.secret_key.to_secret_bytes());

let raw_msg = format!("[\"EVENT\",{}]", note.json().unwrap());
info!("sending {}", raw_msg);
pool.send(&enostr::ClientMessage::raw(raw_msg));
draft.clear();
}
}
}
}
5 changes: 5 additions & 0 deletions src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ impl Route {
Route::Timeline(TimelineRoute::Reply(replying_to))
}

pub fn quote(quoting: NoteId) -> Self {
Route::Timeline(TimelineRoute::Quote(quoting))
}

pub fn accounts() -> Self {
Route::Accounts(AccountsRoute::Accounts)
}
Expand Down Expand Up @@ -110,6 +114,7 @@ impl fmt::Display for Route {
TimelineRoute::Timeline(name) => write!(f, "{}", name),
TimelineRoute::Thread(_id) => write!(f, "Thread"),
TimelineRoute::Reply(_id) => write!(f, "Reply"),
TimelineRoute::Quote(_id) => write!(f, "Quote"),
},

Route::Relays => write!(f, "Relays"),
Expand Down
65 changes: 55 additions & 10 deletions src/timeline/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ use crate::{
draft::Drafts,
imgcache::ImageCache,
notecache::NoteCache,
post_action_executor::PostActionExecutor,
thread::Threads,
timeline::TimelineId,
ui::{self, note::post::PostResponse},
ui::{
self,
note::{post::PostResponse, QuoteRepostView},
},
};

use enostr::{NoteId, RelayPool};
Expand All @@ -17,6 +21,7 @@ pub enum TimelineRoute {
Timeline(TimelineId),
Thread(NoteId),
Reply(NoteId),
Quote(NoteId),
}

pub enum TimelineRouteResponse {
Expand Down Expand Up @@ -48,8 +53,15 @@ pub fn render_timeline_route(
match route {
TimelineRoute::Timeline(timeline_id) => {
if show_postbox {
if let Some(kp) = accounts.selected_or_first_nsec() {
ui::timeline::postbox_view(ndb, kp, pool, drafts, img_cache, ui);
let kp = accounts.selected_or_first_nsec()?;
let draft = drafts.compose_mut();
let response =
ui::timeline::postbox_view(ndb, kp, draft, img_cache, note_cache, ui);

if let Some(action) = response.action {
PostActionExecutor::execute(kp, &action, pool, draft, |np, seckey| {
np.to_note(seckey)
});
}
}

Expand Down Expand Up @@ -96,18 +108,51 @@ pub fn render_timeline_route(
};

let id = egui::Id::new(("post", col, note.key().unwrap()));
let poster = accounts.selected_or_first_nsec()?;
let draft = drafts.reply_mut(note.id());

let response = egui::ScrollArea::vertical().show(ui, |ui| {
ui::PostReplyView::new(ndb, poster, draft, note_cache, img_cache, &note)
.id_source(id)
.show(ui)
});

if let Some(poster) = accounts.selected_or_first_nsec() {
let response = egui::ScrollArea::vertical().show(ui, |ui| {
ui::PostReplyView::new(ndb, poster, pool, drafts, note_cache, img_cache, &note)
.id_source(id)
.show(ui)
if let Some(action) = &response.inner.action {
PostActionExecutor::execute(poster, action, pool, draft, |np, seckey| {
np.to_reply(seckey, &note)
});
}

Some(TimelineRouteResponse::post(response.inner))
}

TimelineRoute::Quote(id) => {
let txn = Transaction::new(ndb).expect("txn");

Some(TimelineRouteResponse::post(response.inner))
let note = if let Ok(note) = ndb.get_note_by_id(&txn, id.bytes()) {
note
} else {
None
ui.label("Quote of unknown note");
return None;
};

let id = egui::Id::new(("post", col, note.key().unwrap()));

let poster = accounts.selected_or_first_nsec()?;
let draft = drafts.quote_mut(note.id());

let response = egui::ScrollArea::vertical().show(ui, |ui| {
QuoteRepostView::new(ndb, poster, note_cache, img_cache, draft, &note)
.id_source(id)
.show(ui)
});

if let Some(action) = &response.inner.action {
PostActionExecutor::execute(poster, action, pool, draft, |np, seckey| {
np.to_quote(seckey, &note)
});
}
Some(TimelineRouteResponse::post(response.inner))
}
}
}
2 changes: 1 addition & 1 deletion src/ui/note/contents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl egui::Widget for NoteContents<'_> {

/// Render an inline note preview with a border. These are used when
/// notes are references within a note
fn render_note_preview(
pub fn render_note_preview(
ui: &mut egui::Ui,
ndb: &Ndb,
note_cache: &mut NoteCache,
Expand Down
17 changes: 17 additions & 0 deletions src/ui/note/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
pub mod contents;
pub mod options;
pub mod post;
pub mod quote_repost;
pub mod reply;

pub use contents::NoteContents;
pub use options::NoteOptions;
pub use post::{PostAction, PostResponse, PostView};
pub use quote_repost::QuoteRepostView;
pub use reply::PostReplyView;

use crate::{
Expand Down Expand Up @@ -555,9 +557,12 @@ fn render_note_actionbar(
) -> egui::InnerResponse<Option<BarAction>> {
ui.horizontal(|ui| {
let reply_resp = reply_button(ui, note_key);
let quote_resp = quote_repost_button(ui, note_key);

if reply_resp.clicked() {
Some(BarAction::Reply(NoteId::new(*note_id)))
} else if quote_resp.clicked() {
Some(BarAction::Quote(NoteId::new(*note_id)))
} else {
None
}
Expand Down Expand Up @@ -614,3 +619,15 @@ fn repost_icon() -> egui::Image<'static> {
let img_data = egui::include_image!("../../../assets/icons/repost_icon_4x.png");
egui::Image::new(img_data)
}

fn quote_repost_button(ui: &mut egui::Ui, note_key: NoteKey) -> egui::Response {
let (rect, size, resp) =
ui::anim::hover_expand_small(ui, ui.id().with(("repost_anim", note_key)));

let expand_size = 5.0;
let rect = rect.translate(egui::vec2(-(expand_size / 2.0), 0.0));

let put_resp = ui.put(rect, repost_icon().max_width(size));

resp.union(put_resp)
}
Loading

0 comments on commit 2208e68

Please sign in to comment.