mirror of
https://github.com/mikedilger/gossip.git
synced 2024-09-19 11:43:43 +00:00
Replying has been implemented
This commit is contained in:
parent
8b708a1891
commit
7b0d9d6370
@ -8,7 +8,7 @@ use crate::globals::{Globals, GLOBALS};
|
|||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use minion::Minion;
|
use minion::Minion;
|
||||||
use nostr_types::{
|
use nostr_types::{
|
||||||
Event, EventKind, Id, Nip05, PreEvent, PrivateKey, PublicKey, PublicKeyHex, Unixtime, Url,
|
Event, EventKind, Id, Nip05, PreEvent, PrivateKey, PublicKey, PublicKeyHex, Tag, Unixtime, Url,
|
||||||
};
|
};
|
||||||
use relay_picker::{BestRelay, RelayPicker};
|
use relay_picker::{BestRelay, RelayPicker};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -474,6 +474,11 @@ impl Overlord {
|
|||||||
let content: String = serde_json::from_str(&bus_message.json_payload)?;
|
let content: String = serde_json::from_str(&bus_message.json_payload)?;
|
||||||
self.post_textnote(content).await?;
|
self.post_textnote(content).await?;
|
||||||
}
|
}
|
||||||
|
"post_reply" => {
|
||||||
|
let (content, reply_to): (String, Id) =
|
||||||
|
serde_json::from_str(&bus_message.json_payload)?;
|
||||||
|
self.post_reply(content, reply_to).await?;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -704,4 +709,60 @@ impl Overlord {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn post_reply(&mut self, content: String, reply_to: Id) -> Result<(), Error> {
|
||||||
|
let event = {
|
||||||
|
let public_key = match GLOBALS.signer.read().await.public_key() {
|
||||||
|
Some(pk) => pk,
|
||||||
|
None => {
|
||||||
|
warn!("No public key! Not posting");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let pre_event = PreEvent {
|
||||||
|
pubkey: public_key,
|
||||||
|
created_at: Unixtime::now().unwrap(),
|
||||||
|
kind: EventKind::TextNote,
|
||||||
|
tags: vec![Tag::Event {
|
||||||
|
id: reply_to,
|
||||||
|
recommended_relay_url: None, // FIXME - we should pick a URL shared by who I am replying to and myself
|
||||||
|
marker: Some("reply".to_string()),
|
||||||
|
}],
|
||||||
|
content,
|
||||||
|
ots: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
GLOBALS.signer.read().await.sign_preevent(pre_event)?
|
||||||
|
};
|
||||||
|
|
||||||
|
let relays: Vec<DbRelay> = GLOBALS
|
||||||
|
.relays
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(_, r)| if r.post { Some(r.to_owned()) } else { None })
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for relay in relays {
|
||||||
|
// Start a minion for it, if there is none
|
||||||
|
if !self.urls_watching.contains(&Url::new(&relay.url)) {
|
||||||
|
self.start_minion(relay.url.clone()).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send it the event to post
|
||||||
|
debug!("Asking {} to post", &relay.url);
|
||||||
|
|
||||||
|
let _ = self.to_minions.send(BusMessage {
|
||||||
|
target: relay.url.clone(),
|
||||||
|
kind: "post_event".to_string(),
|
||||||
|
json_payload: serde_json::to_string(&event).unwrap(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the message for ourself
|
||||||
|
crate::process::process_new_event(&event, false, None).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
112
src/ui/feed.rs
112
src/ui/feed.rs
@ -1,10 +1,11 @@
|
|||||||
use super::{GossipUi, Page};
|
use super::{GossipUi, Page};
|
||||||
use crate::comms::BusMessage;
|
use crate::comms::BusMessage;
|
||||||
use crate::globals::{Globals, GLOBALS};
|
use crate::globals::{Globals, GLOBALS};
|
||||||
use crate::ui::widgets::CopyButton;
|
use crate::ui::widgets::{CopyButton, ReplyButton};
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use egui::{Align, Color32, Context, Layout, RichText, ScrollArea, Ui, Vec2};
|
use egui::{Align, Color32, Context, Layout, RichText, ScrollArea, TextEdit, Ui, Vec2};
|
||||||
use nostr_types::{EventKind, Id};
|
use nostr_types::{EventKind, Id};
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Frame, ui: &mut Ui) {
|
pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Frame, ui: &mut Ui) {
|
||||||
let feed = GLOBALS.feed.blocking_lock().get();
|
let feed = GLOBALS.feed.blocking_lock().get();
|
||||||
@ -16,49 +17,87 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram
|
|||||||
GLOBALS.desired_events.blocking_read().len()
|
GLOBALS.desired_events.blocking_read().len()
|
||||||
};
|
};
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.with_layout(Layout::right_to_left(Align::TOP), |ui| {
|
||||||
|
ui.with_layout(Layout::top_down(Align::Max), |ui| {
|
||||||
|
if ui
|
||||||
|
.button(&format!("Get {} missing events", desired_count))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
let tx = GLOBALS.to_overlord.clone();
|
||||||
|
let _ = tx.send(BusMessage {
|
||||||
|
target: "overlord".to_string(),
|
||||||
|
kind: "get_missing_events".to_string(),
|
||||||
|
json_payload: serde_json::to_string("").unwrap(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.vertical(|ui| {
|
||||||
if !GLOBALS.signer.blocking_read().is_ready() {
|
if !GLOBALS.signer.blocking_read().is_ready() {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label("You need to ");
|
ui.label("You need to ");
|
||||||
if ui.link("setup your identity").clicked() {
|
if ui.link("setup your identity").clicked() {
|
||||||
app.page = Page::You;
|
app.page = Page::You;
|
||||||
}
|
}
|
||||||
|
ui.label(" to post.");
|
||||||
});
|
});
|
||||||
} else if !GLOBALS.relays.blocking_read().iter().any(|(_, r)| r.post) {
|
} else if !GLOBALS.relays.blocking_read().iter().any(|(_, r)| r.post) {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label("You need to ");
|
ui.label("You need to ");
|
||||||
if ui.link("choose relays to post to").clicked() {
|
if ui.link("choose relays").clicked() {
|
||||||
app.page = Page::Relays;
|
app.page = Page::Relays;
|
||||||
}
|
}
|
||||||
|
ui.label(" to post.");
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
ui.text_edit_multiline(&mut app.draft);
|
if let Some(id) = app.replying_to {
|
||||||
|
render_post(app, ctx, frame, ui, id, 0, true);
|
||||||
if ui.button("Send").clicked() && !app.draft.is_empty() {
|
|
||||||
let tx = GLOBALS.to_overlord.clone();
|
|
||||||
let _ = tx.send(BusMessage {
|
|
||||||
target: "overlord".to_string(),
|
|
||||||
kind: "post_textnote".to_string(),
|
|
||||||
json_payload: serde_json::to_string(&app.draft).unwrap(),
|
|
||||||
});
|
|
||||||
app.draft = "".to_owned();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ui.with_layout(Layout::right_to_left(Align::TOP), |ui| {
|
ui.with_layout(Layout::right_to_left(Align::TOP), |ui| {
|
||||||
ui.with_layout(Layout::top_down(Align::Max), |ui| {
|
if ui.button("Send").clicked() && !app.draft.is_empty() {
|
||||||
if ui.button(&format!("Get {} missing events", desired_count)).clicked() {
|
|
||||||
let tx = GLOBALS.to_overlord.clone();
|
let tx = GLOBALS.to_overlord.clone();
|
||||||
let _ = tx.send(BusMessage {
|
match app.replying_to {
|
||||||
target: "overlord".to_string(),
|
Some(_id) => {
|
||||||
kind: "get_missing_events".to_string(),
|
let _ = tx.send(BusMessage {
|
||||||
json_payload: serde_json::to_string("").unwrap(),
|
target: "overlord".to_string(),
|
||||||
});
|
kind: "post_reply".to_string(),
|
||||||
|
json_payload: serde_json::to_string(&(
|
||||||
|
&app.draft,
|
||||||
|
&app.replying_to,
|
||||||
|
))
|
||||||
|
.unwrap(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let _ = tx.send(BusMessage {
|
||||||
|
target: "overlord".to_string(),
|
||||||
|
kind: "post_textnote".to_string(),
|
||||||
|
json_payload: serde_json::to_string(&app.draft).unwrap(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
app.draft = "".to_owned();
|
||||||
|
app.replying_to = None;
|
||||||
}
|
}
|
||||||
|
if ui.button("Cancel").clicked() {
|
||||||
|
app.draft = "".to_owned();
|
||||||
|
app.replying_to = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.add(
|
||||||
|
TextEdit::multiline(&mut app.draft)
|
||||||
|
.hint_text("Type your message here")
|
||||||
|
.desired_width(f32::INFINITY)
|
||||||
|
.lock_focus(true),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
ScrollArea::vertical().show(ui, |ui| {
|
ScrollArea::vertical().show(ui, |ui| {
|
||||||
for id in feed.iter() {
|
for id in feed.iter() {
|
||||||
// Stop rendering at the bottom of the window:
|
// Stop rendering at the bottom of the window:
|
||||||
@ -68,7 +107,7 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram
|
|||||||
// break;
|
// break;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
render_post(app, ctx, frame, ui, *id, 0);
|
render_post(app, ctx, frame, ui, *id, 0, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -80,6 +119,7 @@ fn render_post(
|
|||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
id: Id,
|
id: Id,
|
||||||
indent: usize,
|
indent: usize,
|
||||||
|
as_reply_to: bool,
|
||||||
) {
|
) {
|
||||||
let maybe_event = GLOBALS.events.blocking_read().get(&id).cloned();
|
let maybe_event = GLOBALS.events.blocking_read().get(&id).cloned();
|
||||||
if maybe_event.is_none() {
|
if maybe_event.is_none() {
|
||||||
@ -203,19 +243,27 @@ fn render_post(
|
|||||||
ui.label(&event.content);
|
ui.label(&event.content);
|
||||||
|
|
||||||
// Under row
|
// Under row
|
||||||
ui.horizontal(|ui| {
|
if !as_reply_to {
|
||||||
if ui.add(CopyButton {}).clicked() {
|
ui.horizontal(|ui| {
|
||||||
ui.output().copied_text = event.content.clone();
|
if ui.add(CopyButton {}).clicked() {
|
||||||
}
|
ui.output().copied_text = event.content.clone();
|
||||||
});
|
}
|
||||||
|
|
||||||
|
ui.add_space(24.0);
|
||||||
|
|
||||||
|
if ui.add(ReplyButton {}).clicked() {
|
||||||
|
app.replying_to = Some(event.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
if app.settings.view_threaded {
|
if app.settings.view_threaded && !as_reply_to {
|
||||||
for reply_id in replies {
|
for reply_id in replies {
|
||||||
render_post(app, _ctx, _frame, ui, reply_id, indent + 1);
|
render_post(app, _ctx, _frame, ui, reply_id, indent + 1, as_reply_to);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ use crate::globals::GLOBALS;
|
|||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use eframe::{egui, IconData, Theme};
|
use eframe::{egui, IconData, Theme};
|
||||||
use egui::{ColorImage, Context, ImageData, TextureHandle, TextureOptions};
|
use egui::{ColorImage, Context, ImageData, TextureHandle, TextureOptions};
|
||||||
use nostr_types::{PublicKey, PublicKeyHex};
|
use nostr_types::{Id, PublicKey, PublicKeyHex};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
@ -74,6 +74,7 @@ struct GossipUi {
|
|||||||
password: String,
|
password: String,
|
||||||
import_bech32: String,
|
import_bech32: String,
|
||||||
import_hex: String,
|
import_hex: String,
|
||||||
|
replying_to: Option<Id>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for GossipUi {
|
impl Drop for GossipUi {
|
||||||
@ -139,6 +140,7 @@ impl GossipUi {
|
|||||||
password: "".to_owned(),
|
password: "".to_owned(),
|
||||||
import_bech32: "".to_owned(),
|
import_bech32: "".to_owned(),
|
||||||
import_hex: "".to_owned(),
|
import_hex: "".to_owned(),
|
||||||
|
replying_to: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,5 @@
|
|||||||
mod copy_button;
|
mod copy_button;
|
||||||
pub use copy_button::CopyButton;
|
pub use copy_button::CopyButton;
|
||||||
|
|
||||||
|
mod reply_button;
|
||||||
|
pub use reply_button::ReplyButton;
|
||||||
|
92
src/ui/widgets/reply_button.rs
Normal file
92
src/ui/widgets/reply_button.rs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
use eframe::{egui, epaint};
|
||||||
|
use egui::{Color32, Pos2, Response, Sense, Shape, Ui, Vec2, Widget};
|
||||||
|
use epaint::{PathShape, Stroke};
|
||||||
|
|
||||||
|
pub struct ReplyButton {}
|
||||||
|
|
||||||
|
impl ReplyButton {
|
||||||
|
fn paint(ui: &mut Ui, corner: Pos2) {
|
||||||
|
ui.painter().add(Shape::Path(PathShape {
|
||||||
|
points: vec![
|
||||||
|
Pos2 {
|
||||||
|
x: corner.x + 2.0,
|
||||||
|
y: corner.y + 0.0,
|
||||||
|
},
|
||||||
|
Pos2 {
|
||||||
|
x: corner.x + 14.0,
|
||||||
|
y: corner.y + 0.0,
|
||||||
|
},
|
||||||
|
Pos2 {
|
||||||
|
x: corner.x + 16.0,
|
||||||
|
y: corner.y + 2.0,
|
||||||
|
},
|
||||||
|
Pos2 {
|
||||||
|
x: corner.x + 16.0,
|
||||||
|
y: corner.y + 8.0,
|
||||||
|
},
|
||||||
|
Pos2 {
|
||||||
|
x: corner.x + 14.0,
|
||||||
|
y: corner.y + 10.0,
|
||||||
|
},
|
||||||
|
Pos2 {
|
||||||
|
x: corner.x + 12.0,
|
||||||
|
y: corner.y + 10.0,
|
||||||
|
},
|
||||||
|
Pos2 {
|
||||||
|
x: corner.x + 4.0,
|
||||||
|
y: corner.y + 15.0,
|
||||||
|
},
|
||||||
|
Pos2 {
|
||||||
|
x: corner.x + 6.0,
|
||||||
|
y: corner.y + 10.0,
|
||||||
|
},
|
||||||
|
Pos2 {
|
||||||
|
x: corner.x + 2.0,
|
||||||
|
y: corner.y + 10.0,
|
||||||
|
},
|
||||||
|
Pos2 {
|
||||||
|
x: corner.x + 0.0,
|
||||||
|
y: corner.y + 8.0,
|
||||||
|
},
|
||||||
|
Pos2 {
|
||||||
|
x: corner.x + 0.0,
|
||||||
|
y: corner.y + 2.0,
|
||||||
|
},
|
||||||
|
Pos2 {
|
||||||
|
x: corner.x + 2.0,
|
||||||
|
y: corner.y + 0.0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
closed: true,
|
||||||
|
fill: Color32::TRANSPARENT,
|
||||||
|
stroke: Stroke {
|
||||||
|
width: 1.0,
|
||||||
|
color: Color32::from_rgb(0x8d, 0x7f, 0x73),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for ReplyButton {
|
||||||
|
fn ui(self, ui: &mut Ui) -> Response {
|
||||||
|
let padding = ui.spacing().button_padding;
|
||||||
|
let space = Vec2 {
|
||||||
|
x: 12.0 + padding.x * 2.0,
|
||||||
|
y: 12.0 + padding.y * 2.0,
|
||||||
|
};
|
||||||
|
let (id, rect) = ui.allocate_space(space);
|
||||||
|
let response = ui.interact(rect, id, Sense::click());
|
||||||
|
let shift = if response.is_pointer_button_down_on() {
|
||||||
|
2.0
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
let pos = Pos2 {
|
||||||
|
x: rect.min.x + padding.x + shift,
|
||||||
|
y: rect.min.y + padding.y + shift,
|
||||||
|
};
|
||||||
|
Self::paint(ui, ui.painter().round_pos_to_pixels(pos));
|
||||||
|
|
||||||
|
response
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user