working notes + notes&replies

Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin
2024-05-17 22:53:10 -05:00
parent 232ba0e3aa
commit 6fc5eb27fc
6 changed files with 161 additions and 62 deletions

View File

@@ -1 +1 @@
[{"limit": 100, "kinds":[1], "#p": ["32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"]}]
[{"limit": 500, "kinds":[1], "#p": ["32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"]}]

File diff suppressed because one or more lines are too long

View File

@@ -5,7 +5,7 @@ use crate::frame_history::FrameHistory;
use crate::imgcache::ImageCache;
use crate::notecache::{CachedNote, NoteCache};
use crate::timeline;
use crate::timeline::{NoteRef, Timeline};
use crate::timeline::{NoteRef, Timeline, ViewFilter};
use crate::ui::is_mobile;
use crate::Result;
@@ -96,7 +96,7 @@ fn send_initial_filters(damus: &mut Damus, relay_url: &str) {
for timeline in &damus.timelines {
let mut filter = timeline.filter.clone();
for f in &mut filter {
since_optimize_filter(f, timeline.notes());
since_optimize_filter(f, timeline.notes(ViewFilter::NotesAndReplies));
}
relay.subscribe(format!("initial{}", c), filter);
c += 1;
@@ -165,7 +165,7 @@ fn try_process_event(damus: &mut Damus, ctx: &egui::Context) -> Result<()> {
let txn = Transaction::new(&damus.ndb)?;
let mut unknown_ids: HashSet<UnknownId> = HashSet::new();
for timeline in 0..damus.timelines.len() {
if let Err(err) = poll_notes_for_timeline(ctx, damus, &txn, timeline, &mut unknown_ids) {
if let Err(err) = poll_notes_for_timeline(damus, &txn, timeline, &mut unknown_ids) {
error!("{}", err);
}
}
@@ -268,13 +268,12 @@ fn get_unknown_note_ids<'a>(
}
fn poll_notes_for_timeline<'a>(
ctx: &egui::Context,
damus: &mut Damus,
txn: &'a Transaction,
timeline: usize,
timeline_ind: usize,
ids: &mut HashSet<UnknownId<'a>>,
) -> Result<()> {
let sub = if let Some(sub) = &damus.timelines[timeline].subscription {
let sub = if let Some(sub) = &damus.timelines[timeline_ind].subscription {
sub
} else {
return Err(Error::NoActiveSubscription);
@@ -285,32 +284,78 @@ fn poll_notes_for_timeline<'a>(
debug!("{} new notes! {:?}", new_note_ids.len(), new_note_ids);
}
let new_refs: Vec<NoteRef> = new_note_ids
let new_refs: Vec<(Note, NoteRef)> = new_note_ids
.iter()
.map(|key| {
let note = damus.ndb.get_note_by_key(txn, *key).expect("no note??");
let _ = get_unknown_note_ids(&damus.ndb, txn, &note, *key, ids);
NoteRef {
key: *key,
created_at: note.created_at(),
}
let created_at = note.created_at();
(
note,
NoteRef {
key: *key,
created_at,
},
)
})
.collect();
let timeline = &mut damus.timelines[timeline];
let prev_items = timeline.notes().len();
timeline.current_view_mut().notes = timeline::merge_sorted_vecs(&timeline.notes(), &new_refs);
let new_items = timeline.notes().len() - prev_items;
// ViewFilter::NotesAndReplies
{
let timeline = &mut damus.timelines[timeline_ind];
// TODO: technically items could have been added inbetween
if new_items > 0 {
timeline
.current_view()
.list
.borrow_mut()
.items_inserted_at_start(new_items);
let prev_items = timeline.notes(ViewFilter::NotesAndReplies).len();
let refs: Vec<NoteRef> = new_refs.iter().map(|(_note, nr)| *nr).collect();
timeline.view_mut(ViewFilter::NotesAndReplies).notes =
timeline::merge_sorted_vecs(timeline.notes(ViewFilter::NotesAndReplies), &refs);
let new_items = timeline.notes(ViewFilter::NotesAndReplies).len() - prev_items;
// TODO: technically items could have been added inbetween
if new_items > 0 {
damus.timelines[timeline_ind]
.view(ViewFilter::NotesAndReplies)
.list
.borrow_mut()
.items_inserted_at_start(new_items);
}
}
//
// handle the filtered case (ViewFilter::Notes, no replies)
//
// TODO(jb55): this is mostly just copied from above, let's just use a loop
// I initially tried this but ran into borrow checker issues
{
let mut filtered_refs = Vec::with_capacity(new_refs.len());
for (note, nr) in &new_refs {
let cached_note = damus.note_cache_mut().cached_note_or_insert(nr.key, note);
if ViewFilter::filter_notes(cached_note, note) {
filtered_refs.push(*nr);
}
}
let timeline = &mut damus.timelines[timeline_ind];
let prev_items = timeline.notes(ViewFilter::Notes).len();
timeline.view_mut(ViewFilter::Notes).notes =
timeline::merge_sorted_vecs(timeline.notes(ViewFilter::Notes), &filtered_refs);
let new_items = timeline.notes(ViewFilter::Notes).len() - prev_items;
// TODO: technically items could have been added inbetween
if new_items > 0 {
damus.timelines[timeline_ind]
.view(ViewFilter::Notes)
.list
.borrow_mut()
.items_inserted_at_start(new_items);
}
}
Ok(())
@@ -322,31 +367,48 @@ fn setup_profiling() {
}
fn setup_initial_nostrdb_subs(damus: &mut Damus) -> Result<()> {
for timeline in &mut damus.timelines {
let filters: Vec<nostrdb::Filter> = timeline
let timelines = damus.timelines.len();
for i in 0..timelines {
let filters: Vec<nostrdb::Filter> = damus.timelines[i]
.filter
.iter()
.map(crate::filter::convert_enostr_filter)
.collect();
timeline.subscription = Some(damus.ndb.subscribe(filters.clone())?);
damus.timelines[i].subscription = Some(damus.ndb.subscribe(filters.clone())?);
let txn = Transaction::new(&damus.ndb)?;
debug!(
"querying sub {} {:?}",
timeline.subscription.as_ref().unwrap().id,
timeline.filter
damus.timelines[i].subscription.as_ref().unwrap().id,
damus.timelines[i].filter
);
let res = damus.ndb.query(
let results = damus.ndb.query(
&txn,
filters,
timeline.filter[0].limit.unwrap_or(200) as i32,
damus.timelines[i].filter[0].limit.unwrap_or(200) as i32,
)?;
timeline.notes_view_mut().notes = res
.iter()
.map(|qr| NoteRef {
key: qr.note_key,
created_at: qr.note.created_at(),
})
.collect();
let filters = {
let views = &damus.timelines[i].views;
let filters: Vec<fn(&CachedNote, &Note) -> bool> =
views.iter().map(|v| v.filter.filter()).collect();
filters
};
for result in results {
for (j, filter) in filters.iter().enumerate() {
if filter(
damus
.note_cache_mut()
.cached_note_or_insert_mut(result.note_key, &result.note),
&result.note,
) {
damus.timelines[i].views[j].notes.push(NoteRef {
key: result.note_key,
created_at: result.note.created_at(),
})
}
}
}
}
Ok(())
@@ -385,7 +447,7 @@ fn get_unknown_ids<'a>(txn: &'a Transaction, damus: &Damus) -> Result<Vec<Unknow
let mut ids: HashSet<UnknownId> = HashSet::new();
for timeline in &damus.timelines {
for noteref in timeline.notes() {
for noteref in timeline.notes(ViewFilter::NotesAndReplies) {
let note = damus.ndb.get_note_by_key(txn, noteref.key)?;
let _ = get_unknown_note_ids(&damus.ndb, txn, &note, note.key().unwrap(), &mut ids);
}
@@ -517,11 +579,8 @@ impl Damus {
}
}
pub fn get_note_cache_mut(&mut self, note_key: NoteKey, note: &Note<'_>) -> &mut CachedNote {
self.note_cache
.cache
.entry(note_key)
.or_insert_with(|| CachedNote::new(note))
pub fn note_cache_mut(&mut self) -> &mut NoteCache {
&mut self.note_cache
}
pub fn selected_timeline(&mut self) -> &mut Timeline {
@@ -627,7 +686,9 @@ fn render_panel(ctx: &egui::Context, app: &mut Damus, timeline_ind: usize) {
ui.weak(format!(
"{} notes",
&app.timelines[timeline_ind].notes().len()
&app.timelines[timeline_ind]
.notes(ViewFilter::NotesAndReplies)
.len()
));
}
});

View File

@@ -9,6 +9,20 @@ pub struct NoteCache {
pub cache: HashMap<NoteKey, CachedNote>,
}
impl NoteCache {
pub fn cached_note_or_insert_mut(&mut self, note_key: NoteKey, note: &Note) -> &mut CachedNote {
self.cache
.entry(note_key)
.or_insert_with(|| CachedNote::new(note))
}
pub fn cached_note_or_insert(&mut self, note_key: NoteKey, note: &Note) -> &CachedNote {
self.cache
.entry(note_key)
.or_insert_with(|| CachedNote::new(note))
}
}
pub struct CachedNote {
reltime: TimeCached<String>,
pub reply: NoteReplyBuf,

View File

@@ -48,17 +48,25 @@ impl ViewFilter {
}
}
fn index(&self) -> usize {
pub fn index(&self) -> usize {
match self {
ViewFilter::Notes => 0,
ViewFilter::NotesAndReplies => 1,
}
}
fn filter(&self, cache: &CachedNote, note: &Note) -> bool {
pub fn filter_notes(cache: &CachedNote, note: &Note) -> bool {
!cache.reply.borrow(note.tags()).is_reply()
}
fn identity(_cache: &CachedNote, _note: &Note) -> bool {
true
}
pub fn filter(&self) -> fn(&CachedNote, &Note) -> bool {
match self {
ViewFilter::Notes => !cache.reply.borrow(note.tags()).is_reply(),
ViewFilter::NotesAndReplies => true,
ViewFilter::Notes => ViewFilter::filter_notes,
ViewFilter::NotesAndReplies => ViewFilter::identity,
}
}
}
@@ -138,12 +146,16 @@ impl Timeline {
&mut self.views[self.selected_view as usize]
}
pub fn notes(&self) -> &[NoteRef] {
&self.views[ViewFilter::NotesAndReplies.index()].notes
pub fn notes(&self, view: ViewFilter) -> &[NoteRef] {
&self.views[view.index()].notes
}
pub fn notes_view_mut(&mut self) -> &mut TimelineView {
&mut self.views[ViewFilter::NotesAndReplies.index()]
pub fn view(&self, view: ViewFilter) -> &TimelineView {
&self.views[view.index()]
}
pub fn view_mut(&mut self, view: ViewFilter) -> &mut TimelineView {
&mut self.views[view.index()]
}
}
@@ -163,7 +175,7 @@ fn shrink_range_to_width(range: egui::Rangef, width: f32) -> egui::Rangef {
egui::Rangef::new(min, max)
}
fn tabs_ui(ui: &mut egui::Ui) {
fn tabs_ui(timeline: &mut Timeline, ui: &mut egui::Ui) {
ui.spacing_mut().item_spacing.y = 0.0;
let tab_res = egui_tabs::Tabs::new(2)
@@ -200,6 +212,8 @@ fn tabs_ui(ui: &mut egui::Ui) {
// fun animation
if let Some(sel) = tab_res.selected() {
timeline.selected_view = sel;
let (underline, underline_y) = tab_res.inner()[sel as usize].inner;
let underline_width = underline.span();
@@ -236,19 +250,22 @@ pub fn timeline_view(ui: &mut egui::Ui, app: &mut Damus, timeline: usize) {
let row_height = ui.fonts(|f| f.row_height(&font_id)) + ui.spacing().item_spacing.y;
*/
tabs_ui(ui);
tabs_ui(&mut app.timelines[timeline], ui);
// need this for some reason??
ui.add_space(3.0);
let scroll_id = ui.id().with(app.timelines[timeline].selected_view);
egui::ScrollArea::vertical()
.id_source(scroll_id)
.animated(false)
.scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible)
.show(ui, |ui| {
let view = app.timelines[timeline].current_view();
let len = view.notes.len();
let list = view.list.clone();
list.borrow_mut()
view.list
.clone()
.borrow_mut()
.ui_custom_layout(ui, len, |ui, start_index| {
ui.spacing_mut().item_spacing.y = 0.0;
ui.spacing_mut().item_spacing.x = 4.0;

View File

@@ -50,7 +50,8 @@ fn reply_desc(
puffin::profile_function!();
let note_reply = app
.get_note_cache_mut(note_key, note)
.note_cache_mut()
.cached_note_or_insert_mut(note_key, note)
.reply
.borrow(note.tags());
@@ -157,12 +158,15 @@ impl<'a> Note<'a> {
//ui.horizontal(|ui| {
ui.spacing_mut().item_spacing.x = 2.0;
let note_cache = self.app.get_note_cache_mut(note_key, self.note);
let cached_note = self
.app
.note_cache_mut()
.cached_note_or_insert_mut(note_key, self.note);
let (_id, rect) = ui.allocate_space(egui::vec2(50.0, 20.0));
ui.allocate_rect(rect, Sense::hover());
ui.put(rect, |ui: &mut egui::Ui| {
render_reltime(ui, note_cache, false).response
render_reltime(ui, cached_note, false).response
});
let (_id, rect) = ui.allocate_space(egui::vec2(150.0, 20.0));
ui.allocate_rect(rect, Sense::hover());
@@ -250,8 +254,11 @@ impl<'a> Note<'a> {
.abbreviated(20),
);
let note_cache = self.app.get_note_cache_mut(note_key, self.note);
render_reltime(ui, note_cache, true);
let cached_note = self
.app
.note_cache_mut()
.cached_note_or_insert_mut(note_key, self.note);
render_reltime(ui, cached_note, true);
});
ui.horizontal(|ui| {