integrate new threads conception

Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
kernelkind
2025-06-17 12:49:09 -04:00
parent f6753bae97
commit d560e84eab
10 changed files with 260 additions and 185 deletions

View File

@@ -41,8 +41,9 @@ struct NoteActionResponse {
#[allow(clippy::too_many_arguments)]
fn execute_note_action(
action: NoteAction,
ndb: &Ndb,
ndb: &mut Ndb,
timeline_cache: &mut TimelineCache,
threads: &mut Threads,
note_cache: &mut NoteCache,
pool: &mut RelayPool,
txn: &Transaction,
@@ -52,6 +53,7 @@ fn execute_note_action(
images: &mut Images,
router_type: RouterType,
ui: &mut egui::Ui,
col: usize,
) -> NoteActionResponse {
let mut timeline_res = None;
let mut router_action = None;
@@ -74,13 +76,16 @@ fn execute_note_action(
break 'ex;
};
let kind = TimelineKind::Thread(thread_selection);
router_action = Some(RouterAction::route_to(Route::Timeline(kind.clone())));
// NOTE!!: you need the note_id to timeline root id thing
timeline_res = threads
.open(ndb, txn, pool, &thread_selection, preview, col)
.map(NotesOpenResult::Thread);
timeline_res = timeline_cache
.open(ndb, note_cache, txn, pool, &kind)
.map(NotesOpenResult::Timeline);
let route = Route::Thread(thread_selection);
router_action = Some(RouterAction::Overlay {
route,
make_new: preview,
});
}
NoteAction::Hashtag(htag) => {
let kind = TimelineKind::Hashtag(htag.clone());
@@ -151,10 +156,11 @@ fn execute_note_action(
#[allow(clippy::too_many_arguments)]
pub fn execute_and_process_note_action(
action: NoteAction,
ndb: &Ndb,
ndb: &mut Ndb,
columns: &mut Columns,
col: usize,
timeline_cache: &mut TimelineCache,
threads: &mut Threads,
note_cache: &mut NoteCache,
pool: &mut RelayPool,
txn: &Transaction,
@@ -179,6 +185,7 @@ pub fn execute_and_process_note_action(
action,
ndb,
timeline_cache,
threads,
note_cache,
pool,
txn,
@@ -188,6 +195,7 @@ pub fn execute_and_process_note_action(
images,
router_type,
ui,
col,
);
if let Some(br) = resp.timeline_res {
@@ -195,7 +203,9 @@ pub fn execute_and_process_note_action(
NotesOpenResult::Timeline(timeline_open_result) => {
timeline_open_result.process(ndb, note_cache, txn, timeline_cache, unknown_ids);
}
NotesOpenResult::Thread(new_thread_notes) => todo!(),
NotesOpenResult::Thread(thread_open_result) => {
thread_open_result.process(threads, ndb, txn, unknown_ids, note_cache);
}
}
}
@@ -258,7 +268,7 @@ impl NewNotes {
unknown_ids: &mut UnknownIds,
note_cache: &mut NoteCache,
) {
let reversed = matches!(&self.id, TimelineKind::Thread(_));
let reversed = false;
let timeline = if let Some(profile) = timeline_cache.timelines.get_mut(&self.id) {
profile

View File

@@ -8,7 +8,7 @@ use crate::{
storage,
subscriptions::{SubKind, Subscriptions},
support::Support,
timeline::{self, TimelineCache},
timeline::{self, thread::Threads, TimelineCache},
ui::{self, DesktopSidePanel},
view_state::ViewState,
Result,
@@ -45,6 +45,7 @@ pub struct Damus {
pub subscriptions: Subscriptions,
pub support: Support,
pub jobs: JobsCache,
pub threads: Threads,
//frame_history: crate::frame_history::FrameHistory,
@@ -443,6 +444,8 @@ impl Damus {
ctx.accounts.with_fallback(FALLBACK_PUBKEY());
let threads = Threads::default();
Self {
subscriptions: Subscriptions::default(),
since_optimize: parsed_args.since_optimize,
@@ -458,6 +461,7 @@ impl Damus {
debug,
unrecognized_args,
jobs,
threads,
}
}
@@ -502,6 +506,7 @@ impl Damus {
decks_cache,
unrecognized_args: BTreeSet::default(),
jobs: JobsCache::default(),
threads: Threads::default(),
}
}

View File

@@ -8,7 +8,10 @@ use crate::{
profile_state::ProfileState,
relay_pool_manager::RelayPoolManager,
route::{Route, Router, SingletonRouter},
timeline::{route::render_timeline_route, TimelineCache},
timeline::{
route::{render_thread_route, render_timeline_route},
TimelineCache,
},
ui::{
self,
add_column::render_add_column_routes,
@@ -210,7 +213,7 @@ fn process_nav_resp(
if let Some(action) = response.action {
match action {
NavAction::Returned(_) => {
NavAction::Returned(return_type) => {
let r = app
.columns_mut(ctx.accounts)
.column_mut(col)
@@ -223,6 +226,12 @@ fn process_nav_resp(
}
};
if let Some(Route::Thread(selection)) = &r {
tracing::info!("Return type: {:?}", return_type);
app.threads
.close(ctx.ndb, ctx.pool, selection, return_type, col);
}
process_result = Some(ProcessNavResult::SwitchOccurred);
}
@@ -355,6 +364,7 @@ fn process_render_nav_action(
get_active_columns_mut(ctx.accounts, &mut app.decks_cache),
col,
&mut app.timeline_cache,
&mut app.threads,
ctx.note_cache,
ctx.pool,
&txn,
@@ -426,6 +436,17 @@ fn render_nav_body(
&mut note_context,
&mut app.jobs,
),
Route::Thread(selection) => render_thread_route(
ctx.unknown_ids,
&mut app.threads,
ctx.accounts,
selection,
col,
app.note_options,
ui,
&mut note_context,
&mut app.jobs,
),
Route::Accounts(amr) => {
let mut action = render_accounts_route(
ui,

View File

@@ -1,5 +1,5 @@
use enostr::{NoteId, Pubkey};
use notedeck::{NoteZapTargetOwned, WalletType};
use notedeck::{NoteZapTargetOwned, RootNoteIdBuf, WalletType};
use std::{
fmt::{self},
ops::Range,
@@ -20,6 +20,7 @@ use tokenator::{ParseError, TokenParser, TokenSerializable, TokenWriter};
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum Route {
Timeline(TimelineKind),
Thread(ThreadSelection),
Accounts(AccountsRoute),
Reply(NoteId),
Quote(NoteId),
@@ -53,7 +54,7 @@ impl Route {
}
pub fn thread(thread_selection: ThreadSelection) -> Self {
Route::Timeline(TimelineKind::Thread(thread_selection))
Route::Thread(thread_selection)
}
pub fn profile(pubkey: Pubkey) -> Self {
@@ -79,6 +80,18 @@ impl Route {
pub fn serialize_tokens(&self, writer: &mut TokenWriter) {
match self {
Route::Timeline(timeline_kind) => timeline_kind.serialize_tokens(writer),
Route::Thread(selection) => {
writer.write_token("thread");
if let Some(reply) = selection.selected_note {
writer.write_token("root");
writer.write_token(&NoteId::new(*selection.root_id.bytes()).hex());
writer.write_token("reply");
writer.write_token(&reply.hex());
} else {
writer.write_token(&NoteId::new(*selection.root_id.bytes()).hex());
}
}
Route::Accounts(routes) => routes.serialize_tokens(writer),
Route::AddColumn(routes) => routes.serialize_tokens(writer),
Route::Search => writer.write_token("search"),
@@ -199,6 +212,31 @@ impl Route {
Ok(Route::Search)
})
},
|p| {
p.parse_all(|p| {
p.parse_token("thread")?;
p.parse_token("root")?;
let root = tokenator::parse_hex_id(p)?;
p.parse_token("reply")?;
let selected = tokenator::parse_hex_id(p)?;
Ok(Route::Thread(ThreadSelection {
root_id: RootNoteIdBuf::new_unsafe(root),
selected_note: Some(NoteId::new(selected)),
}))
})
},
|p| {
p.parse_all(|p| {
p.parse_token("thread")?;
Ok(Route::Thread(ThreadSelection::from_root_id(
RootNoteIdBuf::new_unsafe(tokenator::parse_hex_id(p)?),
)))
})
},
],
)
}
@@ -206,6 +244,7 @@ impl Route {
pub fn title(&self) -> ColumnTitle<'_> {
match self {
Route::Timeline(kind) => kind.to_title(),
Route::Thread(_) => ColumnTitle::simple("Thread"),
Route::Reply(_id) => ColumnTitle::simple("Reply"),
Route::Quote(_id) => ColumnTitle::simple("Quote"),
Route::Relays => ColumnTitle::simple("Relays"),
@@ -423,9 +462,9 @@ impl fmt::Display for Route {
TimelineKind::Generic(_) => write!(f, "Custom"),
TimelineKind::Search(_) => write!(f, "Search"),
TimelineKind::Hashtag(ht) => write!(f, "Hashtag ({})", ht),
TimelineKind::Thread(_id) => write!(f, "Thread"),
TimelineKind::Profile(_id) => write!(f, "Profile"),
},
Route::Thread(_) => write!(f, "Thread"),
Route::Reply(_id) => write!(f, "Reply"),
Route::Quote(_id) => write!(f, "Quote"),
Route::Relays => write!(f, "Relays"),
@@ -482,3 +521,30 @@ impl<R: Clone> Default for SingletonRouter<R> {
}
}
}
#[cfg(test)]
mod tests {
use enostr::NoteId;
use tokenator::{TokenParser, TokenWriter};
use crate::{timeline::ThreadSelection, Route};
use enostr::Pubkey;
use notedeck::RootNoteIdBuf;
#[test]
fn test_thread_route_serialize() {
let note_id_hex = "1c54e5b0c386425f7e017d9e068ddef8962eb2ce1bb08ed27e24b93411c12e60";
let note_id = NoteId::from_hex(note_id_hex).unwrap();
let data_str = format!("thread:{}", note_id_hex);
let data = &data_str.split(":").collect::<Vec<&str>>();
let mut token_writer = TokenWriter::default();
let mut parser = TokenParser::new(&data);
let parsed = Route::parse(&mut parser, &Pubkey::new(*note_id.bytes())).unwrap();
let expected = Route::Thread(ThreadSelection::from_root_id(RootNoteIdBuf::new_unsafe(
*note_id.bytes(),
)));
parsed.serialize_tokens(&mut token_writer);
assert_eq!(expected, parsed);
assert_eq!(token_writer.str(), data_str);
}
}

View File

@@ -208,8 +208,6 @@ pub enum TimelineKind {
Profile(Pubkey),
Thread(ThreadSelection),
Universe,
/// Generic filter, references a hash of a filter
@@ -266,7 +264,6 @@ impl Display for TimelineKind {
TimelineKind::Profile(_) => f.write_str("Profile"),
TimelineKind::Universe => f.write_str("Universe"),
TimelineKind::Hashtag(_) => f.write_str("Hashtag"),
TimelineKind::Thread(_) => f.write_str("Thread"),
TimelineKind::Search(_) => f.write_str("Search"),
}
}
@@ -282,7 +279,6 @@ impl TimelineKind {
TimelineKind::Universe => None,
TimelineKind::Generic(_) => None,
TimelineKind::Hashtag(_ht) => None,
TimelineKind::Thread(_ht) => None,
TimelineKind::Search(query) => query.author(),
}
}
@@ -298,7 +294,6 @@ impl TimelineKind {
TimelineKind::Universe => true,
TimelineKind::Generic(_) => true,
TimelineKind::Hashtag(_ht) => true,
TimelineKind::Thread(_ht) => true,
TimelineKind::Search(_q) => true,
}
}
@@ -321,10 +316,6 @@ impl TimelineKind {
writer.write_token("profile");
PubkeySource::pubkey(*pk).serialize_tokens(writer);
}
TimelineKind::Thread(root_note_id) => {
writer.write_token("thread");
writer.write_token(&root_note_id.root_id.hex());
}
TimelineKind::Universe => {
writer.write_token("universe");
}
@@ -377,12 +368,6 @@ impl TimelineKind {
TokenParser::alt(
parser,
&[
|p| {
p.parse_token("thread")?;
Ok(TimelineKind::Thread(ThreadSelection::from_root_id(
RootNoteIdBuf::new_unsafe(tokenator::parse_hex_id(p)?),
)))
},
|p| {
p.parse_token("universe")?;
Ok(TimelineKind::Universe)
@@ -425,10 +410,6 @@ impl TimelineKind {
TimelineKind::Profile(pk)
}
pub fn thread(selected_note: ThreadSelection) -> Self {
TimelineKind::Thread(selected_note)
}
pub fn is_notifications(&self) -> bool {
matches!(self, TimelineKind::Notifications(_))
}
@@ -474,17 +455,6 @@ impl TimelineKind {
todo!("implement generic filter lookups")
}
TimelineKind::Thread(selection) => FilterState::ready(vec![
nostrdb::Filter::new()
.kinds([1])
.event(selection.root_id.bytes())
.build(),
nostrdb::Filter::new()
.ids([selection.root_id.bytes()])
.limit(1)
.build(),
]),
TimelineKind::Profile(pk) => FilterState::ready(vec![Filter::new()
.authors([pk.bytes()])
.kinds([1])
@@ -510,8 +480,6 @@ impl TimelineKind {
TimelineTab::full_tabs(),
)),
TimelineKind::Thread(root_id) => Some(Timeline::thread(root_id)),
TimelineKind::Generic(_filter_id) => {
warn!("you can't convert a TimelineKind::Generic to a Timeline");
// TODO: you actually can! just need to look up the filter id
@@ -609,7 +577,6 @@ impl TimelineKind {
},
TimelineKind::Notifications(_pubkey_source) => ColumnTitle::simple("Notifications"),
TimelineKind::Profile(_pubkey_source) => ColumnTitle::needs_db(self),
TimelineKind::Thread(_root_id) => ColumnTitle::simple("Thread"),
TimelineKind::Universe => ColumnTitle::simple("Universe"),
TimelineKind::Generic(_) => ColumnTitle::simple("Custom"),
TimelineKind::Hashtag(hashtag) => ColumnTitle::formatted(hashtag.to_string()),

View File

@@ -214,24 +214,6 @@ impl Timeline {
))
}
pub fn thread(selection: ThreadSelection) -> Self {
let filter = vec![
nostrdb::Filter::new()
.kinds([1])
.event(selection.root_id.bytes())
.build(),
nostrdb::Filter::new()
.ids([selection.root_id.bytes()])
.limit(1)
.build(),
];
Timeline::new(
TimelineKind::Thread(selection),
FilterState::ready(filter),
TimelineTab::only_notes_and_replies(),
)
}
pub fn last_per_pubkey(list: &Note, list_kind: &ListKind) -> Result<Self> {
let kind = 1;
let notes_per_pk = 1;

View File

@@ -1,7 +1,7 @@
use crate::{
nav::RenderNavAction,
profile::ProfileAction,
timeline::{TimelineCache, TimelineKind},
timeline::{thread::Threads, ThreadSelection, TimelineCache, TimelineKind},
ui::{self, ProfileView},
};
@@ -16,7 +16,7 @@ pub fn render_timeline_route(
accounts: &mut Accounts,
kind: &TimelineKind,
col: usize,
mut note_options: NoteOptions,
note_options: NoteOptions,
depth: usize,
ui: &mut egui::Ui,
note_context: &mut NoteContext,
@@ -74,32 +74,40 @@ pub fn render_timeline_route(
note_action.map(RenderNavAction::NoteAction)
}
}
TimelineKind::Thread(id) => {
// don't truncate thread notes for now, since they are
// default truncated everywher eelse
note_options.set_truncate(false);
// text is selectable in threads
note_options.set_selectable_text(true);
ui::ThreadView::new(
timeline_cache,
unknown_ids,
id.selected_or_root(),
note_options,
&accounts.mutefun(),
note_context,
&accounts.get_selected_account().map(|a| (&a.key).into()),
jobs,
)
.id_source(egui::Id::new(("threadscroll", col)))
.ui(ui)
.map(Into::into)
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn render_thread_route(
unknown_ids: &mut UnknownIds,
threads: &mut Threads,
accounts: &mut Accounts,
selection: &ThreadSelection,
col: usize,
mut note_options: NoteOptions,
ui: &mut egui::Ui,
note_context: &mut NoteContext,
jobs: &mut JobsCache,
) -> Option<RenderNavAction> {
// don't truncate thread notes for now, since they are
// default truncated everywher eelse
note_options.set_truncate(false);
ui::ThreadView::new(
threads,
unknown_ids,
selection.selected_or_root(),
note_options,
&accounts.mutefun(),
note_context,
&accounts.get_selected_account().map(|a| (&a.key).into()),
jobs,
)
.id_source(col)
.ui(ui)
.map(Into::into)
}
#[allow(clippy::too_many_arguments)]
pub fn render_profile_route(
pubkey: &Pubkey,
@@ -139,30 +147,3 @@ pub fn render_profile_route(
None
}
}
#[cfg(test)]
mod tests {
use enostr::NoteId;
use tokenator::{TokenParser, TokenWriter};
use crate::timeline::{ThreadSelection, TimelineKind};
use enostr::Pubkey;
use notedeck::RootNoteIdBuf;
#[test]
fn test_timeline_route_serialize() {
let note_id_hex = "1c54e5b0c386425f7e017d9e068ddef8962eb2ce1bb08ed27e24b93411c12e60";
let note_id = NoteId::from_hex(note_id_hex).unwrap();
let data_str = format!("thread:{}", note_id_hex);
let data = &data_str.split(":").collect::<Vec<&str>>();
let mut token_writer = TokenWriter::default();
let mut parser = TokenParser::new(&data);
let parsed = TimelineKind::parse(&mut parser, &Pubkey::new(*note_id.bytes())).unwrap();
let expected = TimelineKind::Thread(ThreadSelection::from_root_id(
RootNoteIdBuf::new_unsafe(*note_id.bytes()),
));
parsed.serialize_tokens(&mut token_writer);
assert_eq!(expected, parsed);
assert_eq!(token_writer.str(), data_str);
}
}

View File

@@ -1,6 +1,7 @@
use crate::column::ColumnsAction;
use crate::nav::RenderNavAction;
use crate::nav::SwitchingAction;
use crate::timeline::ThreadSelection;
use crate::{
column::Columns,
route::Route,
@@ -437,11 +438,6 @@ impl<'a> NavTitle<'a> {
TimelineKind::Profile(pubkey) => Some(self.show_profile(ui, pubkey, pfp_size)),
TimelineKind::Thread(_) => {
// no pfp for threads
None
}
TimelineKind::Search(_sq) => {
// TODO: show author pfp if author field set?
@@ -467,6 +463,9 @@ impl<'a> NavTitle<'a> {
Route::Search => Some(ui.add(ui::side_panel::search_button())),
Route::Wallet(_) => None,
Route::CustomizeZapAmount(_) => None,
Route::Thread(thread_selection) => {
Some(self.thread_pfp(ui, thread_selection, pfp_size))
}
}
}
@@ -488,6 +487,23 @@ impl<'a> NavTitle<'a> {
}
}
fn thread_pfp(
&mut self,
ui: &mut egui::Ui,
selection: &ThreadSelection,
pfp_size: f32,
) -> egui::Response {
let txn = Transaction::new(self.ndb).unwrap();
if let Ok(note) = self.ndb.get_note_by_id(&txn, selection.selected_or_root()) {
if let Some(mut pfp) = self.pubkey_pfp(&txn, note.pubkey(), pfp_size) {
return ui.add(&mut pfp);
}
}
ui.add(&mut ProfilePic::new(self.img_cache, notedeck::profile::no_pfp_url()).size(pfp_size))
}
fn title_label_value(title: &str) -> egui::Label {
egui::Label::new(RichText::new(title).text_style(NotedeckTextStyle::Body.text_style()))
.selectable(false)

View File

@@ -3,21 +3,19 @@ use egui_virtual_list::VirtualList;
use enostr::KeypairUnowned;
use nostrdb::{Note, Transaction};
use notedeck::note::root_note_id_from_selected_id;
use notedeck::{MuteFun, NoteAction, NoteContext, RootNoteId, UnknownIds};
use notedeck::{MuteFun, NoteAction, NoteContext, UnknownIds};
use notedeck_ui::jobs::JobsCache;
use notedeck_ui::note::NoteResponse;
use notedeck_ui::{NoteOptions, NoteView};
use tracing::error;
use crate::timeline::thread::NoteSeenFlags;
use crate::timeline::{ThreadSelection, TimelineCache, TimelineKind};
use crate::ui::timeline::TimelineTabView;
use crate::timeline::thread::{NoteSeenFlags, ParentState, Threads};
pub struct ThreadView<'a, 'd> {
timeline_cache: &'a mut TimelineCache,
threads: &'a mut Threads,
unknown_ids: &'a mut UnknownIds,
selected_note_id: &'a [u8; 32],
note_options: NoteOptions,
col: usize,
id_source: egui::Id,
is_muted: &'a MuteFun,
note_context: &'a mut NoteContext<'d>,
@@ -28,7 +26,7 @@ pub struct ThreadView<'a, 'd> {
impl<'a, 'd> ThreadView<'a, 'd> {
#[allow(clippy::too_many_arguments)]
pub fn new(
timeline_cache: &'a mut TimelineCache,
threads: &'a mut Threads,
unknown_ids: &'a mut UnknownIds,
selected_note_id: &'a [u8; 32],
note_options: NoteOptions,
@@ -39,7 +37,7 @@ impl<'a, 'd> ThreadView<'a, 'd> {
) -> Self {
let id_source = egui::Id::new("threadscroll_threadview");
ThreadView {
timeline_cache,
threads,
unknown_ids,
selected_note_id,
note_options,
@@ -48,11 +46,13 @@ impl<'a, 'd> ThreadView<'a, 'd> {
note_context,
cur_acc,
jobs,
col: 0,
}
}
pub fn id_source(mut self, id: egui::Id) -> Self {
self.id_source = id;
pub fn id_source(mut self, col: usize) -> Self {
self.col = col;
self.id_source = egui::Id::new(("threadscroll", col));
self
}
@@ -73,62 +73,88 @@ impl<'a, 'd> ThreadView<'a, 'd> {
scroll_area = scroll_area.vertical_scroll_offset(offset);
}
let output = scroll_area.show(ui, |ui| {
let root_id = match RootNoteId::new(
self.note_context.ndb,
self.note_context.note_cache,
&txn,
self.selected_note_id,
) {
Ok(root_id) => root_id,
Err(err) => {
ui.label(format!("Error loading thread: {:?}", err));
return None;
}
};
let thread_timeline = self
.timeline_cache
.notes(
self.note_context.ndb,
self.note_context.note_cache,
&txn,
&TimelineKind::Thread(ThreadSelection::from_root_id(root_id.to_owned())),
)
.get_ptr();
// TODO(jb55): skip poll if ThreadResult is fresh?
let reversed = true;
// poll for new notes and insert them into our existing notes
if let Err(err) = thread_timeline.poll_notes_into_view(
self.note_context.ndb,
&txn,
self.unknown_ids,
self.note_context.note_cache,
reversed,
) {
error!("error polling notes into thread timeline: {err}");
}
TimelineTabView::new(
thread_timeline.current_view(),
true,
self.note_options,
&txn,
self.is_muted,
self.note_context,
self.cur_acc,
self.jobs,
)
.show(ui)
});
let output = scroll_area.show(ui, |ui| self.notes(ui, &txn));
ui.data_mut(|d| d.insert_temp(offset_id, output.state.offset.y));
output.inner
}
fn notes(&mut self, ui: &mut egui::Ui, txn: &Transaction) -> Option<NoteAction> {
let Ok(cur_note) = self
.note_context
.ndb
.get_note_by_id(txn, self.selected_note_id)
else {
let id = *self.selected_note_id;
tracing::error!("ndb: Did not find note {}", enostr::NoteId::new(id).hex());
return None;
};
self.threads.update(
&cur_note,
self.note_context.note_cache,
self.note_context.ndb,
txn,
self.unknown_ids,
self.col,
);
let cur_node = self.threads.threads.get(&self.selected_note_id).unwrap();
let full_chain = cur_node.have_all_ancestors;
let mut note_builder = ThreadNoteBuilder::new(cur_note);
let mut parent_state = cur_node.prev.clone();
while let ParentState::Parent(id) = parent_state {
if let Ok(note) = self.note_context.ndb.get_note_by_id(txn, id.bytes()) {
note_builder.add_chain(note);
if let Some(res) = self.threads.threads.get(&id.bytes()) {
parent_state = res.prev.clone();
continue;
}
}
parent_state = ParentState::Unknown;
}
for note_ref in &cur_node.replies {
if let Ok(note) = self.note_context.ndb.get_note_by_key(txn, note_ref.key) {
note_builder.add_reply(note);
}
}
let list = &mut self
.threads
.threads
.get_mut(&self.selected_note_id)
.unwrap()
.list;
let notes = note_builder.into_notes(&mut self.threads.seen_flags);
if !full_chain {
// TODO(kernelkind): insert UI denoting we don't have the full chain yet
ui.colored_label(ui.visuals().error_fg_color, "LOADING NOTES");
}
let zapping_acc = self
.cur_acc
.as_ref()
.filter(|_| self.note_context.current_account_has_wallet)
.or(self.cur_acc.as_ref());
show_notes(
ui,
list,
&notes,
self.note_context,
zapping_acc,
self.note_options,
self.jobs,
txn,
self.is_muted,
)
}
}
#[allow(clippy::too_many_arguments)]