-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is a really dumb and broken version of threads, but it will be our foundation for future changes. All it currently does is load whatever notes we have locally for a thread in chronological order. It currently does not open any subscriptions. It is not clear what is replying to what, but hey, its a start. Signed-off-by: William Casarin <[email protected]>
- Loading branch information
Showing
5 changed files
with
178 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
use crate::note::NoteRef; | ||
use crate::timeline::{TimelineView, ViewFilter}; | ||
use nostrdb::{Ndb, Transaction}; | ||
use std::collections::HashMap; | ||
use tracing::debug; | ||
|
||
#[derive(Default)] | ||
pub struct Thread { | ||
pub view: TimelineView, | ||
} | ||
|
||
impl Thread { | ||
pub fn new(notes: Vec<NoteRef>) -> Self { | ||
let mut cap = ((notes.len() as f32) * 1.5) as usize; | ||
if cap == 0 { | ||
cap = 25; | ||
} | ||
let mut view = TimelineView::new_with_capacity(ViewFilter::NotesAndReplies, cap); | ||
view.notes = notes; | ||
|
||
Thread { view } | ||
} | ||
} | ||
|
||
#[derive(Default)] | ||
pub struct Threads { | ||
threads: HashMap<[u8; 32], Thread>, | ||
} | ||
|
||
impl Threads { | ||
pub fn thread_mut(&mut self, ndb: &Ndb, txn: &Transaction, root_id: &[u8; 32]) -> &mut Thread { | ||
// we can't use the naive hashmap entry API here because lookups | ||
// require a copy, wait until we have a raw entry api. We could | ||
// also use hashbrown? | ||
|
||
if self.threads.contains_key(root_id) { | ||
return self.threads.get_mut(root_id).unwrap(); | ||
} | ||
|
||
// looks like we don't have this thread yet, populate it | ||
// TODO: should we do this in the caller? | ||
let root = if let Ok(root) = ndb.get_note_by_id(txn, root_id) { | ||
root | ||
} else { | ||
debug!("couldnt find root note for id {}", hex::encode(root_id)); | ||
self.threads.insert(root_id.to_owned(), Thread::new(vec![])); | ||
return self.threads.get_mut(root_id).unwrap(); | ||
}; | ||
|
||
// we don't have the thread, query for it! | ||
let filter = nostrdb::Filter::new().event(root.id()).build(); | ||
|
||
// TODO: what should be the max results ? | ||
let notes = if let Ok(mut results) = ndb.query(txn, vec![filter], 10000) { | ||
results.reverse(); | ||
results | ||
.into_iter() | ||
.map(NoteRef::from_query_result) | ||
.collect() | ||
} else { | ||
debug!( | ||
"got no results from thread lookup for {}", | ||
hex::encode(root.id()) | ||
); | ||
vec![] | ||
}; | ||
|
||
debug!("found thread with {} notes", notes.len()); | ||
self.threads.insert(root_id.to_owned(), Thread::new(notes)); | ||
self.threads.get_mut(root_id).unwrap() | ||
} | ||
|
||
//fn thread_by_id(&self, ndb: &Ndb, id: &[u8; 32]) -> &mut Thread { | ||
//} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
use crate::{ui, Damus}; | ||
use nostrdb::{Note, NoteReply}; | ||
use tracing::warn; | ||
|
||
pub struct ThreadView<'a> { | ||
app: &'a mut Damus, | ||
timeline: usize, | ||
selected_note: &'a Note<'a>, | ||
} | ||
|
||
impl<'a> ThreadView<'a> { | ||
pub fn new(app: &'a mut Damus, timeline: usize, selected_note: &'a Note<'a>) -> Self { | ||
ThreadView { | ||
app, | ||
timeline, | ||
selected_note, | ||
} | ||
} | ||
|
||
pub fn ui(&mut self, ui: &mut egui::Ui) { | ||
let txn = self.selected_note.txn().unwrap(); | ||
let key = self.selected_note.key().unwrap(); | ||
let scroll_id = egui::Id::new(( | ||
"threadscroll", | ||
self.app.timelines[self.timeline].selected_view, | ||
self.timeline, | ||
key, | ||
)); | ||
egui::ScrollArea::vertical() | ||
.id_source(scroll_id) | ||
.animated(false) | ||
.auto_shrink([false, false]) | ||
.scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysVisible) | ||
.show(ui, |ui| { | ||
let root_id = NoteReply::new(self.selected_note.tags()) | ||
.root() | ||
.map_or_else(|| self.selected_note.id(), |nr| nr.id); | ||
|
||
let (len, list) = { | ||
let thread = self.app.threads.thread_mut(&self.app.ndb, txn, root_id); | ||
let len = thread.view.notes.len(); | ||
(len, &mut thread.view.list) | ||
}; | ||
|
||
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; | ||
|
||
let note_key = { | ||
let thread = self.app.threads.thread_mut(&self.app.ndb, txn, root_id); | ||
thread.view.notes[start_index].key | ||
}; | ||
|
||
let txn = self.selected_note.txn().unwrap(); | ||
|
||
let note = if let Ok(note) = self.app.ndb.get_note_by_key(txn, note_key) { | ||
note | ||
} else { | ||
warn!("failed to query note {:?}", note_key); | ||
return 0; | ||
}; | ||
|
||
ui::padding(8.0, ui, |ui| { | ||
let textmode = self.app.textmode; | ||
let resp = ui::NoteView::new(self.app, ¬e) | ||
.note_previews(!textmode) | ||
.show(ui); | ||
|
||
if let Some(action) = resp.action { | ||
action.execute(self.app, self.timeline, note.id()); | ||
} | ||
}); | ||
|
||
ui::hline(ui); | ||
//ui.add(egui::Separator::default().spacing(0.0)); | ||
|
||
1 | ||
}); | ||
}); | ||
} | ||
} |