working notes + notes&replies
Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
@@ -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
147
src/app.rs
147
src/app.rs
@@ -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, ¬e, *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, ¬e, 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()
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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| {
|
||||
|
||||
Reference in New Issue
Block a user