mirror of
https://github.com/aljazceru/notedeck.git
synced 2025-12-24 03:24:21 +01:00
dave: initial note rendering
Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -3272,6 +3272,7 @@ dependencies = [
|
|||||||
"eframe",
|
"eframe",
|
||||||
"egui",
|
"egui",
|
||||||
"egui-wgpu",
|
"egui-wgpu",
|
||||||
|
"enostr",
|
||||||
"futures",
|
"futures",
|
||||||
"hex",
|
"hex",
|
||||||
"nostrdb",
|
"nostrdb",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ notedeck = { workspace = true }
|
|||||||
notedeck_ui = { workspace = true }
|
notedeck_ui = { workspace = true }
|
||||||
eframe = { workspace = true }
|
eframe = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
|
enostr = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
egui-wgpu = { workspace = true }
|
egui-wgpu = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ use egui::{Align, Key, KeyboardShortcut, Layout, Modifiers};
|
|||||||
use egui_wgpu::RenderState;
|
use egui_wgpu::RenderState;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use nostrdb::Transaction;
|
use nostrdb::Transaction;
|
||||||
use notedeck::AppContext;
|
use notedeck::{AppContext, NoteContext};
|
||||||
use notedeck_ui::icons::search_icon;
|
use notedeck_ui::{icons::search_icon, NoteOptions};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::mpsc::{self, Receiver};
|
use std::sync::mpsc::{self, Receiver};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -69,7 +69,7 @@ impl Dave {
|
|||||||
|
|
||||||
let system_prompt = Message::System(format!(
|
let system_prompt = Message::System(format!(
|
||||||
r#"
|
r#"
|
||||||
You are an AI agent for the nostr protocol called Dave, created by Damus. nostr is a decentralized social media and internet communications protocol. You are embedded in a nostr browser called 'Damus Notedeck'. The returned note results are formatted into clickable note widgets. This happens when a nostr-uri is detected (ie: nostr:neventnevent1y4mvl8046gjsvdvztnp7jvs7w29pxcmkyj5p58m7t0dmjc8qddzsje0zmj). When referencing notes, ensure that this uri is included in the response so notes can be rendered inline.
|
You are an AI agent for the nostr protocol called Dave, created by Damus. nostr is a decentralized social media and internet communications protocol. You are embedded in a nostr browser called 'Damus Notedeck'.
|
||||||
|
|
||||||
- The current date is {date} ({timestamp} unix timestamp if needed for queries).
|
- The current date is {date} ({timestamp} unix timestamp if needed for queries).
|
||||||
|
|
||||||
@@ -79,8 +79,9 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
|
|||||||
|
|
||||||
# Response Guidelines
|
# Response Guidelines
|
||||||
|
|
||||||
- You *MUST* include nostr:nevent references when referring to notes
|
- You *MUST* call the present_notes tool with a list of comma-separated nevent references when referring to notes so that the UI can display them. Do *NOT* include nevent references in the text response, but you *SHOULD* use ^1, ^2, etc to reference note indices passed to present_notes.
|
||||||
- When a user asks for a digest instead of specific query terms, make sure to include both `since` and `until` to pull notes for the correct range.
|
- When a user asks for a digest instead of specific query terms, make sure to include both since and until to pull notes for the correct range.
|
||||||
|
- When tasked with open-ended queries such as looking for interesting notes or summarizing the day, make sure to add enough notes to the context (limit: 100-200) so that it returns enough data for summarization.
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -123,6 +124,13 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
|
|||||||
for call in &toolcalls {
|
for call in &toolcalls {
|
||||||
// execute toolcall
|
// execute toolcall
|
||||||
match call.calls() {
|
match call.calls() {
|
||||||
|
ToolCalls::PresentNotes(_note_ids) => {
|
||||||
|
self.chat.push(Message::ToolResponse(ToolResponse::new(
|
||||||
|
call.id().to_owned(),
|
||||||
|
ToolResponses::PresentNotes,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
ToolCalls::Query(search_call) => {
|
ToolCalls::Query(search_call) => {
|
||||||
let resp = search_call.execute(&txn, app_ctx.ndb);
|
let resp = search_call.execute(&txn, app_ctx.ndb);
|
||||||
self.chat.push(Message::ToolResponse(ToolResponse::new(
|
self.chat.push(Message::ToolResponse(ToolResponse::new(
|
||||||
@@ -159,7 +167,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, app_ctx: &AppContext, ui: &mut egui::Ui) {
|
fn render(&mut self, app_ctx: &mut AppContext, ui: &mut egui::Ui) {
|
||||||
// Scroll area for chat messages
|
// Scroll area for chat messages
|
||||||
egui::Frame::NONE.show(ui, |ui| {
|
egui::Frame::NONE.show(ui, |ui| {
|
||||||
ui.with_layout(Layout::bottom_up(Align::Min), |ui| {
|
ui.with_layout(Layout::bottom_up(Align::Min), |ui| {
|
||||||
@@ -186,7 +194,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
|
|||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
Self::chat_frame(ui.ctx()).show(ui, |ui| {
|
Self::chat_frame(ui.ctx()).show(ui, |ui| {
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
self.render_chat(ui);
|
self.render_chat(app_ctx, ui);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -194,7 +202,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_chat(&self, ui: &mut egui::Ui) {
|
fn render_chat(&self, ctx: &mut AppContext, ui: &mut egui::Ui) {
|
||||||
for message in &self.chat {
|
for message in &self.chat {
|
||||||
match message {
|
match message {
|
||||||
Message::User(msg) => self.user_chat(msg, ui),
|
Message::User(msg) => self.user_chat(msg, ui),
|
||||||
@@ -205,7 +213,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
|
|||||||
// have a debug option to show this
|
// have a debug option to show this
|
||||||
}
|
}
|
||||||
Message::ToolCalls(toolcalls) => {
|
Message::ToolCalls(toolcalls) => {
|
||||||
Self::tool_call_ui(toolcalls, ui);
|
Self::tool_call_ui(ctx, toolcalls, ui);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,10 +240,44 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tool_call_ui(toolcalls: &[ToolCall], ui: &mut egui::Ui) {
|
fn tool_call_ui(ctx: &mut AppContext, toolcalls: &[ToolCall], ui: &mut egui::Ui) {
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
for call in toolcalls {
|
for call in toolcalls {
|
||||||
match call.calls() {
|
match call.calls() {
|
||||||
|
ToolCalls::PresentNotes(call) => {
|
||||||
|
let mut note_context = NoteContext {
|
||||||
|
ndb: ctx.ndb,
|
||||||
|
img_cache: ctx.img_cache,
|
||||||
|
note_cache: ctx.note_cache,
|
||||||
|
zaps: ctx.zaps,
|
||||||
|
pool: ctx.pool,
|
||||||
|
};
|
||||||
|
|
||||||
|
let txn = Transaction::new(note_context.ndb).unwrap();
|
||||||
|
|
||||||
|
egui::ScrollArea::horizontal().show(ui, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
for note_id in &call.note_ids {
|
||||||
|
let Ok(note) =
|
||||||
|
note_context.ndb.get_note_by_id(&txn, note_id.bytes())
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: remove current account thing, just add to note context
|
||||||
|
notedeck_ui::NoteView::new(
|
||||||
|
&mut note_context,
|
||||||
|
&None,
|
||||||
|
¬e,
|
||||||
|
NoteOptions::default(),
|
||||||
|
)
|
||||||
|
.preview_style()
|
||||||
|
.show(ui);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ToolCalls::Query(search_call) => {
|
ToolCalls::Query(search_call) => {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
egui::Frame::new()
|
egui::Frame::new()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use async_openai::types::*;
|
use async_openai::types::*;
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
|
use enostr::NoteId;
|
||||||
use nostrdb::{Ndb, Note, NoteKey, Transaction};
|
use nostrdb::{Ndb, Note, NoteKey, Transaction};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
@@ -76,6 +77,7 @@ pub struct QueryResponse {
|
|||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum ToolResponses {
|
pub enum ToolResponses {
|
||||||
Query(QueryResponse),
|
Query(QueryResponse),
|
||||||
|
PresentNotes,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -116,6 +118,7 @@ impl PartialToolCall {
|
|||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum ToolCalls {
|
pub enum ToolCalls {
|
||||||
Query(QueryCall),
|
Query(QueryCall),
|
||||||
|
PresentNotes(PresentNotesCall),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToolCalls {
|
impl ToolCalls {
|
||||||
@@ -129,12 +132,14 @@ impl ToolCalls {
|
|||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::Query(_) => "search",
|
Self::Query(_) => "search",
|
||||||
|
Self::PresentNotes(_) => "present",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arguments(&self) -> String {
|
fn arguments(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Self::Query(search) => serde_json::to_string(search).unwrap(),
|
Self::Query(search) => serde_json::to_string(search).unwrap(),
|
||||||
|
Self::PresentNotes(call) => serde_json::to_string(&call.to_simple()).unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -289,6 +294,51 @@ pub enum QueryContext {
|
|||||||
Any,
|
Any,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called by dave when he wants to display notes on the screen
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub struct PresentNotesCall {
|
||||||
|
pub note_ids: Vec<NoteId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PresentNotesCall {
|
||||||
|
fn to_simple(&self) -> PresentNotesCallSimple {
|
||||||
|
let note_ids = self
|
||||||
|
.note_ids
|
||||||
|
.iter()
|
||||||
|
.map(|nid| hex::encode(nid.bytes()))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(",");
|
||||||
|
|
||||||
|
PresentNotesCallSimple { note_ids }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called by dave when he wants to display notes on the screen
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub struct PresentNotesCallSimple {
|
||||||
|
note_ids: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PresentNotesCall {
|
||||||
|
fn parse(args: &str) -> Result<ToolCalls, ToolCallError> {
|
||||||
|
match serde_json::from_str::<PresentNotesCallSimple>(args) {
|
||||||
|
Ok(call) => {
|
||||||
|
let note_ids = call
|
||||||
|
.note_ids
|
||||||
|
.split(",")
|
||||||
|
.filter_map(|n| NoteId::from_hex(n).ok())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(ToolCalls::PresentNotes(PresentNotesCall { note_ids }))
|
||||||
|
}
|
||||||
|
Err(e) => Err(ToolCallError::ArgParseFailure(format!(
|
||||||
|
"Failed to parse args: '{}', error: {}",
|
||||||
|
args, e
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The parsed nostrdb query that dave wants to use to satisfy a request
|
/// The parsed nostrdb query that dave wants to use to satisfy a request
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct QueryCall {
|
pub struct QueryCall {
|
||||||
@@ -385,17 +435,20 @@ impl QueryCall {
|
|||||||
/// tool responses
|
/// tool responses
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
struct SimpleNote {
|
struct SimpleNote {
|
||||||
|
note_id: String,
|
||||||
pubkey: String,
|
pubkey: String,
|
||||||
name: String,
|
name: String,
|
||||||
content: String,
|
content: String,
|
||||||
created_at: String,
|
created_at: String,
|
||||||
note_kind: String, // todo: add replying to
|
note_kind: u64, // todo: add replying to
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take the result of a tool response and present it to the ai so that
|
/// Take the result of a tool response and present it to the ai so that
|
||||||
/// it can interepret it and take further action
|
/// it can interepret it and take further action
|
||||||
fn format_tool_response_for_ai(txn: &Transaction, ndb: &Ndb, resp: &ToolResponses) -> String {
|
fn format_tool_response_for_ai(txn: &Transaction, ndb: &Ndb, resp: &ToolResponses) -> String {
|
||||||
match resp {
|
match resp {
|
||||||
|
ToolResponses::PresentNotes => "".to_string(),
|
||||||
|
|
||||||
ToolResponses::Query(search_r) => {
|
ToolResponses::Query(search_r) => {
|
||||||
let simple_notes: Vec<SimpleNote> = search_r
|
let simple_notes: Vec<SimpleNote> = search_r
|
||||||
.notes
|
.notes
|
||||||
@@ -415,7 +468,8 @@ fn format_tool_response_for_ai(txn: &Transaction, ndb: &Ndb, resp: &ToolResponse
|
|||||||
|
|
||||||
let content = note.content().to_owned();
|
let content = note.content().to_owned();
|
||||||
let pubkey = hex::encode(note.pubkey());
|
let pubkey = hex::encode(note.pubkey());
|
||||||
let note_kind = note_kind_desc(note.kind() as u64);
|
let note_kind = note.kind() as u64;
|
||||||
|
let note_id = hex::encode(note.id());
|
||||||
|
|
||||||
let created_at = {
|
let created_at = {
|
||||||
let datetime =
|
let datetime =
|
||||||
@@ -424,6 +478,7 @@ fn format_tool_response_for_ai(txn: &Transaction, ndb: &Ndb, resp: &ToolResponse
|
|||||||
};
|
};
|
||||||
|
|
||||||
Some(SimpleNote {
|
Some(SimpleNote {
|
||||||
|
note_id,
|
||||||
pubkey,
|
pubkey,
|
||||||
name,
|
name,
|
||||||
content,
|
content,
|
||||||
@@ -438,7 +493,7 @@ fn format_tool_response_for_ai(txn: &Transaction, ndb: &Ndb, resp: &ToolResponse
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn note_kind_desc(kind: u64) -> String {
|
fn _note_kind_desc(kind: u64) -> String {
|
||||||
match kind {
|
match kind {
|
||||||
1 => "microblog".to_string(),
|
1 => "microblog".to_string(),
|
||||||
0 => "profile".to_string(),
|
0 => "profile".to_string(),
|
||||||
@@ -446,6 +501,23 @@ fn note_kind_desc(kind: u64) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn present_tool() -> Tool {
|
||||||
|
Tool {
|
||||||
|
name: "present_notes",
|
||||||
|
parse_call: PresentNotesCall::parse,
|
||||||
|
description: "A tool for presenting notes to the user for display. Should be called at the end of a response so that the UI can present the notes referred to in the previous message.",
|
||||||
|
arguments: vec![
|
||||||
|
ToolArg {
|
||||||
|
name: "note_ids",
|
||||||
|
description: "A comma-separated list of hex note ids",
|
||||||
|
typ: ArgType::String,
|
||||||
|
required: true,
|
||||||
|
default: None
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn query_tool() -> Tool {
|
fn query_tool() -> Tool {
|
||||||
Tool {
|
Tool {
|
||||||
name: "query",
|
name: "query",
|
||||||
@@ -505,5 +577,5 @@ fn query_tool() -> Tool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn dave_tools() -> Vec<Tool> {
|
pub fn dave_tools() -> Vec<Tool> {
|
||||||
vec![query_tool()]
|
vec![query_tool(), present_tool()]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,27 +95,10 @@ pub fn render_note_preview(
|
|||||||
*/
|
*/
|
||||||
};
|
};
|
||||||
|
|
||||||
egui::Frame::new()
|
NoteView::new(note_context, cur_acc, ¬e, note_options)
|
||||||
.fill(ui.visuals().noninteractive().weak_bg_fill)
|
.preview_style()
|
||||||
.inner_margin(egui::Margin::same(8))
|
.parent(parent)
|
||||||
.outer_margin(egui::Margin::symmetric(0, 8))
|
.show(ui)
|
||||||
.corner_radius(egui::CornerRadius::same(10))
|
|
||||||
.stroke(egui::Stroke::new(
|
|
||||||
1.0,
|
|
||||||
ui.visuals().noninteractive().bg_stroke.color,
|
|
||||||
))
|
|
||||||
.show(ui, |ui| {
|
|
||||||
NoteView::new(note_context, cur_acc, ¬e, note_options)
|
|
||||||
.actionbar(false)
|
|
||||||
.small_pfp(true)
|
|
||||||
.wide(true)
|
|
||||||
.note_previews(false)
|
|
||||||
.options_button(true)
|
|
||||||
.parent(parent)
|
|
||||||
.is_preview(true)
|
|
||||||
.show(ui)
|
|
||||||
})
|
|
||||||
.inner
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ pub struct NoteView<'a, 'd> {
|
|||||||
cur_acc: &'a Option<KeypairUnowned<'a>>,
|
cur_acc: &'a Option<KeypairUnowned<'a>>,
|
||||||
parent: Option<NoteKey>,
|
parent: Option<NoteKey>,
|
||||||
note: &'a nostrdb::Note<'a>,
|
note: &'a nostrdb::Note<'a>,
|
||||||
|
framed: bool,
|
||||||
flags: NoteOptions,
|
flags: NoteOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +69,7 @@ impl<'a, 'd> NoteView<'a, 'd> {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
flags.set_actionbar(true);
|
flags.set_actionbar(true);
|
||||||
flags.set_note_previews(true);
|
flags.set_note_previews(true);
|
||||||
|
let framed = false;
|
||||||
|
|
||||||
let parent: Option<NoteKey> = None;
|
let parent: Option<NoteKey> = None;
|
||||||
Self {
|
Self {
|
||||||
@@ -76,9 +78,20 @@ impl<'a, 'd> NoteView<'a, 'd> {
|
|||||||
parent,
|
parent,
|
||||||
note,
|
note,
|
||||||
flags,
|
flags,
|
||||||
|
framed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn preview_style(self) -> Self {
|
||||||
|
self.actionbar(false)
|
||||||
|
.small_pfp(true)
|
||||||
|
.frame(true)
|
||||||
|
.wide(true)
|
||||||
|
.note_previews(false)
|
||||||
|
.options_button(true)
|
||||||
|
.is_preview(true)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn textmode(mut self, enable: bool) -> Self {
|
pub fn textmode(mut self, enable: bool) -> Self {
|
||||||
self.options_mut().set_textmode(enable);
|
self.options_mut().set_textmode(enable);
|
||||||
self
|
self
|
||||||
@@ -89,6 +102,11 @@ impl<'a, 'd> NoteView<'a, 'd> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn frame(mut self, enable: bool) -> Self {
|
||||||
|
self.framed = enable;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn small_pfp(mut self, enable: bool) -> Self {
|
pub fn small_pfp(mut self, enable: bool) -> Self {
|
||||||
self.options_mut().set_small_pfp(enable);
|
self.options_mut().set_small_pfp(enable);
|
||||||
self
|
self
|
||||||
@@ -256,47 +274,63 @@ impl<'a, 'd> NoteView<'a, 'd> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn show_impl(&mut self, ui: &mut egui::Ui) -> NoteResponse {
|
||||||
|
let txn = self.note.txn().expect("txn");
|
||||||
|
if let Some(note_to_repost) = get_reposted_note(self.note_context.ndb, txn, self.note) {
|
||||||
|
let profile = self
|
||||||
|
.note_context
|
||||||
|
.ndb
|
||||||
|
.get_profile_by_pubkey(txn, self.note.pubkey());
|
||||||
|
|
||||||
|
let style = NotedeckTextStyle::Small;
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.add_space(2.0);
|
||||||
|
ui.add_sized([20.0, 20.0], repost_icon(ui.visuals().dark_mode));
|
||||||
|
});
|
||||||
|
ui.add_space(6.0);
|
||||||
|
let resp = ui.add(one_line_display_name_widget(
|
||||||
|
ui.visuals(),
|
||||||
|
get_display_name(profile.as_ref().ok()),
|
||||||
|
style,
|
||||||
|
));
|
||||||
|
if let Ok(rec) = &profile {
|
||||||
|
resp.on_hover_ui_at_pointer(|ui| {
|
||||||
|
ui.set_max_width(300.0);
|
||||||
|
ui.add(ProfilePreview::new(rec, self.note_context.img_cache));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let color = ui.style().visuals.noninteractive().fg_stroke.color;
|
||||||
|
ui.add_space(4.0);
|
||||||
|
ui.label(
|
||||||
|
RichText::new("Reposted")
|
||||||
|
.color(color)
|
||||||
|
.text_style(style.text_style()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
NoteView::new(self.note_context, self.cur_acc, ¬e_to_repost, self.flags).show(ui)
|
||||||
|
} else {
|
||||||
|
self.show_standard(ui)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn show(&mut self, ui: &mut egui::Ui) -> NoteResponse {
|
pub fn show(&mut self, ui: &mut egui::Ui) -> NoteResponse {
|
||||||
if self.options().has_textmode() {
|
if self.options().has_textmode() {
|
||||||
NoteResponse::new(self.textmode_ui(ui))
|
NoteResponse::new(self.textmode_ui(ui))
|
||||||
|
} else if self.framed {
|
||||||
|
egui::Frame::new()
|
||||||
|
.fill(ui.visuals().noninteractive().weak_bg_fill)
|
||||||
|
.inner_margin(egui::Margin::same(8))
|
||||||
|
.outer_margin(egui::Margin::symmetric(0, 8))
|
||||||
|
.corner_radius(egui::CornerRadius::same(10))
|
||||||
|
.stroke(egui::Stroke::new(
|
||||||
|
1.0,
|
||||||
|
ui.visuals().noninteractive().bg_stroke.color,
|
||||||
|
))
|
||||||
|
.show(ui, |ui| self.show_impl(ui))
|
||||||
|
.inner
|
||||||
} else {
|
} else {
|
||||||
let txn = self.note.txn().expect("txn");
|
self.show_impl(ui)
|
||||||
if let Some(note_to_repost) = get_reposted_note(self.note_context.ndb, txn, self.note) {
|
|
||||||
let profile = self
|
|
||||||
.note_context
|
|
||||||
.ndb
|
|
||||||
.get_profile_by_pubkey(txn, self.note.pubkey());
|
|
||||||
|
|
||||||
let style = NotedeckTextStyle::Small;
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.vertical(|ui| {
|
|
||||||
ui.add_space(2.0);
|
|
||||||
ui.add_sized([20.0, 20.0], repost_icon(ui.visuals().dark_mode));
|
|
||||||
});
|
|
||||||
ui.add_space(6.0);
|
|
||||||
let resp = ui.add(one_line_display_name_widget(
|
|
||||||
ui.visuals(),
|
|
||||||
get_display_name(profile.as_ref().ok()),
|
|
||||||
style,
|
|
||||||
));
|
|
||||||
if let Ok(rec) = &profile {
|
|
||||||
resp.on_hover_ui_at_pointer(|ui| {
|
|
||||||
ui.set_max_width(300.0);
|
|
||||||
ui.add(ProfilePreview::new(rec, self.note_context.img_cache));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let color = ui.style().visuals.noninteractive().fg_stroke.color;
|
|
||||||
ui.add_space(4.0);
|
|
||||||
ui.label(
|
|
||||||
RichText::new("Reposted")
|
|
||||||
.color(color)
|
|
||||||
.text_style(style.text_style()),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
NoteView::new(self.note_context, self.cur_acc, ¬e_to_repost, self.flags).show(ui)
|
|
||||||
} else {
|
|
||||||
self.show_standard(ui)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user